├── CMake ├── FindHiredis.cmake ├── FindLibgit2.cmake ├── FindLibmemcached.cmake ├── FindLibmysql.cmake └── FindSQLite3.cmake ├── memcached ├── CMakeLists.txt └── memcached.c ├── mysql ├── CMakeLists.txt └── mysql.c ├── redis ├── CMakeLists.txt └── hiredis.c └── sqlite ├── CMakeLists.txt └── sqlite.c /CMake/FindHiredis.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find the Lib library 2 | # Once done this will define 3 | # 4 | # LIBHIREDIS_FOUND - System has Hiredis 5 | # LIBHIREDIS_INCLUDE_DIR - The Hiredis include directory 6 | # LIBHIREDIS_LIBRARIES - The libraries needed to use Hiredis 7 | # LIBHIREDIS_DEFINITIONS - Compiler switches required for using Hiredis 8 | 9 | 10 | # use pkg-config to get the directories and then use these values 11 | # in the FIND_PATH() and FIND_LIBRARY() calls 12 | #FIND_PACKAGE(PkgConfig) 13 | #PKG_SEARCH_MODULE(PC_LIBHIREDIS libhiredis) 14 | 15 | SET(LIBHIREDIS_DEFINITIONS ${PC_LIBHIREDIS_CFLAGS_OTHER}) 16 | 17 | FIND_PATH(LIBHIREDIS_INCLUDE_DIR hiredis/hiredis.h 18 | HINTS 19 | ${PC_LIBHIREDIS_INCLUDEDIR} 20 | ${PC_LIBHIREDIS_INCLUDE_DIRS} 21 | ) 22 | 23 | FIND_LIBRARY(LIBHIREDIS_LIBRARIES NAMES hiredis 24 | HINTS 25 | ${PC_LIBHIREDIS_LIBDIR} 26 | ${PC_LIBHIREDIS_LIBRARY_DIRS} 27 | ) 28 | 29 | INCLUDE(FindPackageHandleStandardArgs) 30 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LibHiredis DEFAULT_MSG LIBHIREDIS_LIBRARIES LIBHIREDIS_INCLUDE_DIR) 31 | 32 | #MARK_AS_ADVANCED(LIBHIREDIS_INCLUDE_DIR LIBHIREDIS_LIBRARIES) 33 | -------------------------------------------------------------------------------- /CMake/FindLibgit2.cmake: -------------------------------------------------------------------------------- 1 | # - Try to find the libgit2 library 2 | # Once done this will define 3 | # 4 | # LIBGIT2_FOUND - System has libgit2 5 | # LIBGIT2_INCLUDE_DIR - The libgit2 include directory 6 | # LIBGIT2_LIBRARIES - The libraries needed to use libgit2 7 | # LIBGIT2_DEFINITIONS - Compiler switches required for using libgit2 8 | 9 | 10 | # use pkg-config to get the directories and then use these values 11 | # in the FIND_PATH() and FIND_LIBRARY() calls 12 | #FIND_PACKAGE(PkgConfig) 13 | #PKG_SEARCH_MODULE(PC_LIBGIT2 libgit2) 14 | 15 | SET(LIBGIT2_DEFINITIONS ${PC_LIBGIT2_CFLAGS_OTHER}) 16 | 17 | FIND_PATH(LIBGIT2_INCLUDE_DIR NAMES git2.h 18 | HINTS 19 | ${PC_LIBGIT2_INCLUDEDIR} 20 | ${PC_LIBGIT2_INCLUDE_DIRS} 21 | ) 22 | 23 | FIND_LIBRARY(LIBGIT2_LIBRARIES NAMES git2 24 | HINTS 25 | ${PC_LIBGIT2_LIBDIR} 26 | ${PC_LIBGIT2_LIBRARY_DIRS} 27 | ) 28 | 29 | 30 | INCLUDE(FindPackageHandleStandardArgs) 31 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(libgit2 DEFAULT_MSG LIBGIT2_LIBRARIES LIBGIT2_INCLUDE_DIR) 32 | 33 | MARK_AS_ADVANCED(LIBGIT2_INCLUDE_DIR LIBGIT2_LIBRARIES) 34 | -------------------------------------------------------------------------------- /CMake/FindLibmemcached.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # $Id$ 3 | # 4 | # - Find libmemcached 5 | # Find libmemcached 6 | # 7 | # LIBMEMCACHED_INCLUDE_DIR - where to find libmemcached/memcached.h, etc. 8 | # LIBMEMCACHED_LIBRARY - List of libraries when using libmemcached. 9 | # LIBMEMCACHED_FOUND - True if libmemcached found. 10 | 11 | 12 | IF (LIBMEMCACHED_INCLUDE_DIR) 13 | # Already in cache, be silent 14 | SET(LIBMEMCACHED_FIND_QUIETLY TRUE) 15 | ENDIF () 16 | 17 | FIND_PATH(LIBMEMCACHED_INCLUDE_DIR libmemcached/memcached.h) 18 | 19 | FIND_LIBRARY(LIBMEMCACHED_LIBRARY memcached) 20 | 21 | # handle the QUIETLY and REQUIRED arguments and set Libmemcached_FOUND to TRUE if 22 | # all listed variables are TRUE 23 | INCLUDE(FindPackageHandleStandardArgs) 24 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBMEMCACHED DEFAULT_MSG LIBMEMCACHED_LIBRARY LIBMEMCACHED_INCLUDE_DIR) 25 | 26 | SET(LIBMEMCACHED_VERSION 0) 27 | 28 | IF(LIBMEMCACHED_FOUND) 29 | if (EXISTS "${LIBMEMCACHED_INCLUDE_DIR}/libmemcached/configure.h") 30 | FILE(READ "${LIBMEMCACHED_INCLUDE_DIR}/libmemcached/configure.h" _MEMCACHE_VERSION_CONENTS) 31 | STRING(REGEX REPLACE ".*#define LIBMEMCACHED_VERSION_STRING \"([0-9.]+)\".*" "\\1" LIBMEMCACHED_VERSION "${_MEMCACHE_VERSION_CONENTS}") 32 | endif() 33 | ENDIF() 34 | 35 | SET(LIBMEMCACHED_VERSION ${LIBMEMCACHED_VERSION} CACHE STRING "Version number of libmemcached") 36 | 37 | MARK_AS_ADVANCED(LIBMEMCACHED_LIBRARY LIBMEMCACHED_INCLUDE_DIR LIBMEMCACHED_VERSION) -------------------------------------------------------------------------------- /CMake/FindLibmysql.cmake: -------------------------------------------------------------------------------- 1 | # 2 | # $Id$ 3 | # 4 | # - Find libmysql 5 | # Find libmysql 6 | # 7 | # LIBMYSQL_INCLUDE_DIR - where to find mysql.h, etc. 8 | # LIBMYSQL_LIBRARY - List of libraries when using libmysql. 9 | # LIBMYSQL_FOUND - True if libmysql found. 10 | 11 | 12 | IF (LIBMYSQL_INCLUDE_DIR) 13 | # Already in cache, be silent 14 | SET(LIBMYSQL_FIND_QUIETLY TRUE) 15 | ENDIF () 16 | 17 | FIND_PATH(LIBMYSQL_INCLUDE_DIR mysql.h) 18 | 19 | FIND_LIBRARY(LIBMYSQL_LIBRARY mysqlclient) 20 | 21 | # handle the QUIETLY and REQUIRED arguments and set Libmysql_FOUND to TRUE if 22 | # all listed variables are TRUE 23 | INCLUDE(FindPackageHandleStandardArgs) 24 | FIND_PACKAGE_HANDLE_STANDARD_ARGS(LIBMYSQL DEFAULT_MSG LIBMYSQL_LIBRARY LIBMYSQL_INCLUDE_DIR) 25 | 26 | SET(LIBMYSQL_VERSION 0) 27 | 28 | IF(LIBMYSQL_FOUND) 29 | if (EXISTS "${LIBMYSQL_INCLUDE_DIR}/mysql_version.h") 30 | FILE(READ "${LIBMYSQL_INCLUDE_DIR}/mysql_version.h" _MYSQL_VERSION_CONENTS) 31 | STRING(REGEX REPLACE ".*#define MYSQL_SERVER_VERSION\\s+\"([0-9.]+)\".*" "\\1" LIBMYSQL_VERSION "${_MYSQL_VERSION_CONENTS}") 32 | endif() 33 | ENDIF() 34 | 35 | SET(LIBMYSQL_VERSION ${LIBMYSQL_VERSION} CACHE STRING "Version number of libmysql") 36 | 37 | MARK_AS_ADVANCED(LIBMYSQL_LIBRARY LIBMYSQL_INCLUDE_DIR LIBMYSQL_VERSION) -------------------------------------------------------------------------------- /CMake/FindSQLite3.cmake: -------------------------------------------------------------------------------- 1 | # - find Sqlite 3 2 | # SQLITE3_INCLUDE_DIR - Where to find Sqlite 3 header files (directory) 3 | # SQLITE3_LIBRARIES - Sqlite 3 libraries 4 | # SQLITE3_LIBRARY_RELEASE - Where the release library is 5 | # SQLITE3_LIBRARY_DEBUG - Where the debug library is 6 | # SQLITE3_FOUND - Set to TRUE if we found everything (library, includes and executable) 7 | 8 | # Copyright (c) 2010 Pau Garcia i Quiles, 9 | # 10 | # Redistribution and use is allowed according to the terms of the BSD license. 11 | # For details see the accompanying COPYING-CMAKE-SCRIPTS file. 12 | # 13 | # Generated by CModuler, a CMake Module Generator - http://gitorious.org/cmoduler 14 | 15 | IF( SQLITE3_INCLUDE_DIR AND SQLITE3_LIBRARY_RELEASE AND SQLITE3_LIBRARY_DEBUG ) 16 | SET(SQLITE3_FIND_QUIETLY TRUE) 17 | ENDIF( SQLITE3_INCLUDE_DIR AND SQLITE3_LIBRARY_RELEASE AND SQLITE3_LIBRARY_DEBUG ) 18 | 19 | FIND_PATH( SQLITE3_INCLUDE_DIR sqlite3.h ) 20 | 21 | FIND_LIBRARY(SQLITE3_LIBRARY_RELEASE NAMES sqlite3 ) 22 | 23 | FIND_LIBRARY(SQLITE3_LIBRARY_DEBUG NAMES sqlite3 sqlite3d HINTS /usr/lib/debug/usr/lib/ ) 24 | 25 | IF( SQLITE3_LIBRARY_RELEASE OR SQLITE3_LIBRARY_DEBUG AND SQLITE3_INCLUDE_DIR ) 26 | SET( SQLITE3_FOUND TRUE ) 27 | ENDIF( SQLITE3_LIBRARY_RELEASE OR SQLITE3_LIBRARY_DEBUG AND SQLITE3_INCLUDE_DIR ) 28 | 29 | IF( SQLITE3_LIBRARY_DEBUG AND SQLITE3_LIBRARY_RELEASE ) 30 | # if the generator supports configuration types then set 31 | # optimized and debug libraries, or if the CMAKE_BUILD_TYPE has a value 32 | IF( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE ) 33 | SET( SQLITE3_LIBRARIES optimized ${SQLITE3_LIBRARY_RELEASE} debug ${SQLITE3_LIBRARY_DEBUG} ) 34 | ELSE( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE ) 35 | # if there are no configuration types and CMAKE_BUILD_TYPE has no value 36 | # then just use the release libraries 37 | SET( SQLITE3_LIBRARIES ${SQLITE3_LIBRARY_RELEASE} ) 38 | ENDIF( CMAKE_CONFIGURATION_TYPES OR CMAKE_BUILD_TYPE ) 39 | ELSEIF( SQLITE3_LIBRARY_RELEASE ) 40 | SET( SQLITE3_LIBRARIES ${SQLITE3_LIBRARY_RELEASE} ) 41 | ELSE( SQLITE3_LIBRARY_DEBUG AND SQLITE3_LIBRARY_RELEASE ) 42 | SET( SQLITE3_LIBRARIES ${SQLITE3_LIBRARY_DEBUG} ) 43 | ENDIF( SQLITE3_LIBRARY_DEBUG AND SQLITE3_LIBRARY_RELEASE ) 44 | 45 | IF( SQLITE3_FOUND ) 46 | IF( NOT SQLITE3_FIND_QUIETLY ) 47 | MESSAGE( STATUS "Found Sqlite3 header file in ${SQLITE3_INCLUDE_DIR}") 48 | MESSAGE( STATUS "Found Sqlite3 libraries: ${SQLITE3_LIBRARIES}") 49 | ENDIF( NOT SQLITE3_FIND_QUIETLY ) 50 | ELSE(SQLITE3_FOUND) 51 | IF( SQLITE3_FIND_REQUIRED) 52 | MESSAGE( FATAL_ERROR "Could not find Sqlite3" ) 53 | ELSE( SQLITE3_FIND_REQUIRED) 54 | MESSAGE( STATUS "Optional package Sqlite3 was not found" ) 55 | ENDIF( SQLITE3_FIND_REQUIRED) 56 | ENDIF(SQLITE3_FOUND) 57 | -------------------------------------------------------------------------------- /memcached/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PROJECT(LIBGIT2-memcached C) 2 | CMAKE_MINIMUM_REQUIRED(VERSION 2.6) 3 | 4 | INCLUDE(../CMake/FindLibgit2.cmake) 5 | INCLUDE(../CMake/FindLibmemcached.cmake) 6 | 7 | # Build options 8 | OPTION (BUILD_SHARED_LIBS "Build Shared Library (OFF for Static)" ON) 9 | OPTION (BUILD_TESTS "Build Tests" ON) 10 | 11 | # Build Release by default 12 | IF (NOT CMAKE_BUILD_TYPE) 13 | SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) 14 | ENDIF () 15 | 16 | # Compile and link LIBGIT2 17 | INCLUDE_DIRECTORIES(${LIBGIT2_INCLUDE_DIRS} ${LIBMEMCACHED_INCLUDE_DIR}) 18 | ADD_LIBRARY(git2-memcached memcached.c) 19 | TARGET_LINK_LIBRARIES(git2-memcached ${LIBGIT2_LIBRARIES} ${LIBMEMCACHED_LIBRARY}) 20 | -------------------------------------------------------------------------------- /memcached/memcached.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License, version 2, 4 | * as published by the Free Software Foundation. 5 | * 6 | * In addition to the permissions in the GNU General Public License, 7 | * the authors give you unlimited permission to link the compiled 8 | * version of this file into combinations with other programs, 9 | * and to distribute those combinations without any restriction 10 | * coming from the use of this file. (The General Public License 11 | * restrictions do apply in other respects; for example, they cover 12 | * modification of the file, and distribution when not linked into 13 | * a combined executable.) 14 | * 15 | * This file is distributed in the hope that it will be useful, but 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | * General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; see the file COPYING. If not, write to 22 | * the Free Software Foundation, 51 Franklin Street, Fifth Floor, 23 | * Boston, MA 02110-1301, USA. 24 | */ 25 | 26 | #include 27 | #include 28 | 29 | #include 30 | #include "git2/odb_backend.h" 31 | #include 32 | 33 | typedef struct { 34 | git_odb_backend parent; 35 | memcached_st *db; 36 | } memcached_backend; 37 | 38 | // Since memcached is just a key/value store, we'll use key suffixes 39 | // to denote the different "fields" we want to store for an object 40 | 41 | // the type of object 42 | static const char *type_suffix = ":type"; 43 | 44 | // store the size so we can know how big an object is 45 | // without needing to fetch the data down 46 | static const char *size_suffix = ":size"; 47 | 48 | // the raw object data 49 | static const char *data_suffix = ":data"; 50 | 51 | static char *memcached_backend__build_key(const unsigned char *id, const char *suffix, size_t *out_len) 52 | { 53 | char *new_key; 54 | 55 | *out_len = 20+strlen(suffix); 56 | new_key = malloc(*out_len); 57 | if (!new_key) 58 | return NULL; 59 | 60 | memcpy(new_key, id, 20); 61 | memcpy(new_key+20, suffix, strlen(suffix)); 62 | 63 | return new_key; 64 | } 65 | 66 | int memcached_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *_backend, const git_oid *oid) 67 | { 68 | memcached_backend *backend; 69 | memcached_return ret = 0; 70 | char *type_key, *size_key; 71 | size_t type_key_len, type_value_len, size_key_len, *size_value, size_value_len; 72 | uint32_t type_flags, size_flags; 73 | int status; 74 | 75 | assert(len_p && type_p && _backend && oid); 76 | 77 | backend = (memcached_backend *)_backend; 78 | 79 | type_key = memcached_backend__build_key(oid->id, type_suffix, &type_key_len); 80 | if (type_key == NULL) 81 | return GIT_ENOMEM; 82 | 83 | size_key = memcached_backend__build_key(oid->id, size_suffix, &size_key_len); 84 | if (size_key == NULL) 85 | return GIT_ENOMEM; 86 | 87 | 88 | memset(type_p, 0, sizeof(type_p)); 89 | type_p = (git_otype *)memcached_get(backend->db, type_key, type_key_len, &type_value_len, &type_flags, &ret); 90 | if (type_p == NULL) { 91 | status = GIT_ENOTFOUND; 92 | goto read_header_cleanup; 93 | } 94 | 95 | memset(size_value, 0, sizeof(size_value)); 96 | memset(len_p, 0, sizeof(len_p)); 97 | size_value = (size_t *)memcached_get(backend->db, size_key, size_key_len, &size_value_len, &size_flags, &ret); 98 | if (size_value == NULL) { 99 | if (type_p) 100 | free(type_p); 101 | 102 | status = GIT_ENOTFOUND; 103 | } else { 104 | *len_p = *size_value; 105 | status = GIT_SUCCESS; 106 | } 107 | 108 | read_header_cleanup: 109 | free(type_key); 110 | free(size_key); 111 | return status; 112 | } 113 | 114 | int memcached_backend__read(void **data_p, size_t *len_p, git_otype *type_p, git_odb_backend *_backend, const git_oid *oid) 115 | { 116 | memcached_backend *backend; 117 | memcached_return ret = 0; 118 | char *type_key, *data_key; 119 | size_t type_key_len, data_key_len, type_value_len; 120 | uint32_t type_flags, data_flags; 121 | int status; 122 | void *type_buffer, *data_buffer; 123 | 124 | assert(data_p && len_p && type_p && _backend && oid); 125 | 126 | backend = (memcached_backend *)_backend; 127 | 128 | type_key = memcached_backend__build_key(oid->id, type_suffix, &type_key_len); 129 | if (type_key == NULL) 130 | return GIT_ENOMEM; 131 | 132 | data_key = memcached_backend__build_key(oid->id, data_suffix, &data_key_len); 133 | if (data_key == NULL) 134 | return GIT_ENOMEM; 135 | 136 | 137 | type_buffer = (void *)memcached_get(backend->db, type_key, type_key_len, &type_value_len, &type_flags, &ret); 138 | if (type_buffer == NULL) { 139 | status = GIT_ENOTFOUND; 140 | goto read_cleanup; 141 | } 142 | 143 | data_buffer = (void *)memcached_get(backend->db, data_key, data_key_len, len_p, &data_flags, &ret); 144 | if (data_buffer == NULL && *len_p > 0) { 145 | 146 | if (type_buffer) 147 | free(type_buffer); 148 | 149 | status = GIT_ENOTFOUND; 150 | } else { 151 | *type_p = *(git_otype *)type_buffer; 152 | free(type_buffer); 153 | 154 | *data_p = data_buffer; 155 | status = GIT_SUCCESS; 156 | } 157 | 158 | read_cleanup: 159 | free(type_key); 160 | free(data_key); 161 | return status; 162 | } 163 | 164 | int memcached_backend__exists(git_odb_backend *_backend, const git_oid *oid) 165 | { 166 | memcached_backend *backend; 167 | memcached_return ret = 0; 168 | int found; 169 | char *type_key; 170 | size_t type_key_len; 171 | 172 | assert(_backend && oid); 173 | 174 | backend = (memcached_backend *)_backend; 175 | found = 0; 176 | 177 | type_key = memcached_backend__build_key(oid->id, type_suffix, &type_key_len); 178 | if (type_key == NULL) 179 | return GIT_ENOMEM; 180 | 181 | // use the ADD command with a value of zero length to check for the existence of a key 182 | // this is because it will let us know if the key already exists 183 | // and do nothing in that case. 184 | // we use SET for storing the object so this zero-length value will be overwritten 185 | // by the actual value we want to store 186 | ret = memcached_add(backend->db, type_key, type_key_len, "", 0, 0, 0); 187 | if (ret == MEMCACHED_DATA_EXISTS) { 188 | // object exists 189 | found = 1; 190 | } 191 | 192 | free(type_key); 193 | return found; 194 | } 195 | 196 | int memcached_backend__write(git_oid *oid, git_odb_backend *_backend, const void *data, size_t len, git_otype type) 197 | { 198 | memcached_backend *backend; 199 | memcached_return ret = 0; 200 | char *type_key, *size_key, *data_key; 201 | size_t type_key_len, size_key_len, data_key_len; 202 | int status; 203 | 204 | assert(oid && _backend && data); 205 | 206 | backend = (memcached_backend *)_backend; 207 | 208 | if ((status = git_odb_hash(oid, data, len, type)) < 0) 209 | return status; 210 | 211 | type_key = memcached_backend__build_key(oid->id, type_suffix, &type_key_len); 212 | if (type_key == NULL) 213 | return GIT_ENOMEM; 214 | 215 | size_key = memcached_backend__build_key(oid->id, size_suffix, &size_key_len); 216 | if (size_key == NULL) 217 | return GIT_ENOMEM; 218 | 219 | data_key = memcached_backend__build_key(oid->id, data_suffix, &data_key_len); 220 | if (data_key == NULL) 221 | return GIT_ENOMEM; 222 | 223 | 224 | ret = memcached_set(backend->db, type_key, type_key_len, (const char *)&type, sizeof(type), 0, 0); 225 | if (ret != MEMCACHED_SUCCESS) { 226 | status = GIT_ERROR; 227 | goto write_cleanup; 228 | } 229 | 230 | ret = memcached_set(backend->db, size_key, size_key_len, (const char *)&len, sizeof(len), 0, 0); 231 | if (ret != MEMCACHED_SUCCESS) { 232 | status = GIT_ERROR; 233 | goto write_cleanup; 234 | } 235 | 236 | ret = memcached_set(backend->db, data_key, data_key_len, (const char *)data, len, 0, 0); 237 | if (ret != MEMCACHED_SUCCESS) { 238 | status = GIT_ERROR; 239 | goto write_cleanup; 240 | } 241 | 242 | status = GIT_SUCCESS; 243 | 244 | write_cleanup: 245 | free(type_key); 246 | free(size_key); 247 | free(data_key); 248 | return status; 249 | } 250 | 251 | void memcached_backend__free(git_odb_backend *_backend) 252 | { 253 | memcached_backend *backend; 254 | assert(_backend); 255 | backend = (memcached_backend *) _backend; 256 | 257 | if (backend->db) 258 | memcached_free(backend->db); 259 | 260 | free(backend); 261 | } 262 | 263 | int git_odb_backend_memcached(git_odb_backend **backend_out, const char *host, int port) 264 | { 265 | memcached_backend *backend; 266 | memcached_return ret = 0; 267 | uint64_t set = 1; 268 | 269 | backend = calloc(1, sizeof (memcached_backend)); 270 | if (backend == NULL) 271 | return GIT_ENOMEM; 272 | 273 | 274 | backend->db = memcached_create(NULL); 275 | if (backend->db == NULL) 276 | goto cleanup; 277 | 278 | ret = memcached_server_add(backend->db, host, port); 279 | if (ret != MEMCACHED_SUCCESS) 280 | goto cleanup; 281 | 282 | // requires memcached 1.3+ 283 | memcached_behavior_set(backend->db, MEMCACHED_BEHAVIOR_BINARY_PROTOCOL, set); 284 | 285 | memcached_behavior_set(backend->db, MEMCACHED_BEHAVIOR_NO_BLOCK, set); 286 | memcached_behavior_set(backend->db, MEMCACHED_BEHAVIOR_TCP_NODELAY, set); 287 | 288 | backend->parent.read = &memcached_backend__read; 289 | backend->parent.read_header = &memcached_backend__read_header; 290 | backend->parent.write = &memcached_backend__write; 291 | backend->parent.exists = &memcached_backend__exists; 292 | backend->parent.free = &memcached_backend__free; 293 | 294 | *backend_out = (git_odb_backend *) backend; 295 | 296 | return GIT_SUCCESS; 297 | 298 | cleanup: 299 | free(backend); 300 | return GIT_ERROR; 301 | } 302 | -------------------------------------------------------------------------------- /mysql/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PROJECT(LIBGIT2-mysql C) 2 | CMAKE_MINIMUM_REQUIRED(VERSION 2.6) 3 | 4 | INCLUDE(../CMake/FindLibgit2.cmake) 5 | INCLUDE(../CMake/FindLibmysql.cmake) 6 | 7 | # Build options 8 | OPTION (BUILD_SHARED_LIBS "Build Shared Library (OFF for Static)" ON) 9 | OPTION (BUILD_TESTS "Build Tests" ON) 10 | 11 | # Build Release by default 12 | IF (NOT CMAKE_BUILD_TYPE) 13 | SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) 14 | ENDIF () 15 | 16 | # Compile and link LIBGIT2 17 | INCLUDE_DIRECTORIES(${LIBGIT2_INCLUDE_DIRS} ${LIBMYSQL_INCLUDE_DIR}) 18 | ADD_LIBRARY(git2-mysql mysql.c) 19 | TARGET_LINK_LIBRARIES(git2-mysql ${LIBGIT2_LIBRARIES} ${LIBMYSQL_LIBRARY}) 20 | -------------------------------------------------------------------------------- /mysql/mysql.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License, version 2, 4 | * as published by the Free Software Foundation. 5 | * 6 | * In addition to the permissions in the GNU General Public License, 7 | * the authors give you unlimited permission to link the compiled 8 | * version of this file into combinations with other programs, 9 | * and to distribute those combinations without any restriction 10 | * coming from the use of this file. (The General Public License 11 | * restrictions do apply in other respects; for example, they cover 12 | * modification of the file, and distribution when not linked into 13 | * a combined executable.) 14 | * 15 | * This file is distributed in the hope that it will be useful, but 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | * General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; see the file COPYING. If not, write to 22 | * the Free Software Foundation, 51 Franklin Street, Fifth Floor, 23 | * Boston, MA 02110-1301, USA. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | /* MySQL C Api docs: 32 | * http://dev.mysql.com/doc/refman/5.1/en/c-api-function-overview.html 33 | * 34 | * And the prepared statement API docs: 35 | * http://dev.mysql.com/doc/refman/5.1/en/c-api-prepared-statement-function-overview.html 36 | */ 37 | #include 38 | 39 | #define GIT2_TABLE_NAME "git2_odb" 40 | #define GIT2_STORAGE_ENGINE "InnoDB" 41 | 42 | typedef struct { 43 | git_odb_backend parent; 44 | MYSQL *db; 45 | MYSQL_STMT *st_read; 46 | MYSQL_STMT *st_write; 47 | MYSQL_STMT *st_read_header; 48 | } mysql_backend; 49 | 50 | int mysql_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *_backend, const git_oid *oid) 51 | { 52 | mysql_backend *backend; 53 | int error; 54 | MYSQL_BIND bind_buffers[1]; 55 | MYSQL_BIND result_buffers[1]; 56 | 57 | assert(len_p && type_p && _backend && oid); 58 | 59 | backend = (mysql_backend *)_backend; 60 | error = GIT_ERROR; 61 | 62 | memset(bind_buffers, 0, sizeof(bind_buffers)); 63 | memset(result_buffers, 0, sizeof(result_buffers)); 64 | 65 | // bind the oid passed to the statement 66 | bind_buffers[0].buffer = (void*)oid->id; 67 | bind_buffers[0].buffer_length = 20; 68 | bind_buffers[0].length = &bind_buffers[0].buffer_length; 69 | bind_buffers[0].buffer_type = MYSQL_TYPE_BLOB; 70 | if (mysql_stmt_bind_param(backend->st_read_header, bind_buffers) != 0) 71 | return 0; 72 | 73 | // execute the statement 74 | if (mysql_stmt_execute(backend->st_read_header) != 0) 75 | return 0; 76 | 77 | if (mysql_stmt_store_result(backend->st_read_header) != 0) 78 | return 0; 79 | 80 | // this should either be 0 or 1 81 | // if it's > 1 MySQL's unique index failed and we should all fear for our lives 82 | if (mysql_stmt_num_rows(backend->st_read_header) == 1) { 83 | result_buffers[0].buffer_type = MYSQL_TYPE_TINY; 84 | result_buffers[0].buffer = type_p; 85 | result_buffers[0].buffer_length = sizeof(*type_p); 86 | memset(type_p, 0, sizeof(*type_p)); 87 | 88 | result_buffers[1].buffer_type = MYSQL_TYPE_LONGLONG; 89 | result_buffers[1].buffer = len_p; 90 | result_buffers[1].buffer_length = sizeof(*len_p); 91 | memset(len_p, 0, sizeof(*len_p)); 92 | 93 | if(mysql_stmt_bind_result(backend->st_read_header, result_buffers) != 0) 94 | return GIT_ERROR; 95 | 96 | // this should populate the buffers at *type_p and *len_p 97 | if(mysql_stmt_fetch(backend->st_read_header) != 0) 98 | return GIT_ERROR; 99 | 100 | error = GIT_OK; 101 | } else { 102 | error = GIT_ENOTFOUND; 103 | } 104 | 105 | // reset the statement for further use 106 | if (mysql_stmt_reset(backend->st_read_header) != 0) 107 | return 0; 108 | 109 | return error; 110 | } 111 | 112 | int mysql_backend__read(void **data_p, size_t *len_p, git_otype *type_p, git_odb_backend *_backend, const git_oid *oid) 113 | { 114 | mysql_backend *backend; 115 | int error; 116 | MYSQL_BIND bind_buffers[1]; 117 | MYSQL_BIND result_buffers[3]; 118 | unsigned long data_len; 119 | 120 | assert(len_p && type_p && _backend && oid); 121 | 122 | backend = (mysql_backend *)_backend; 123 | error = GIT_ERROR; 124 | 125 | memset(bind_buffers, 0, sizeof(bind_buffers)); 126 | memset(result_buffers, 0, sizeof(result_buffers)); 127 | 128 | // bind the oid passed to the statement 129 | bind_buffers[0].buffer = (void*)oid->id; 130 | bind_buffers[0].buffer_length = 20; 131 | bind_buffers[0].length = &bind_buffers[0].buffer_length; 132 | bind_buffers[0].buffer_type = MYSQL_TYPE_BLOB; 133 | if (mysql_stmt_bind_param(backend->st_read, bind_buffers) != 0) 134 | return 0; 135 | 136 | // execute the statement 137 | if (mysql_stmt_execute(backend->st_read) != 0) 138 | return 0; 139 | 140 | if (mysql_stmt_store_result(backend->st_read) != 0) 141 | return 0; 142 | 143 | // this should either be 0 or 1 144 | // if it's > 1 MySQL's unique index failed and we should all fear for our lives 145 | if (mysql_stmt_num_rows(backend->st_read) == 1) { 146 | result_buffers[0].buffer_type = MYSQL_TYPE_TINY; 147 | result_buffers[0].buffer = type_p; 148 | result_buffers[0].buffer_length = sizeof(*type_p); 149 | memset(type_p, 0, sizeof(*type_p)); 150 | 151 | result_buffers[1].buffer_type = MYSQL_TYPE_LONGLONG; 152 | result_buffers[1].buffer = len_p; 153 | result_buffers[1].buffer_length = sizeof(*len_p); 154 | memset(len_p, 0, sizeof(*len_p)); 155 | 156 | // by setting buffer and buffer_length to 0, this tells libmysql 157 | // we want it to set data_len to the *actual* length of that field 158 | // this way we can malloc exactly as much memory as we need for the buffer 159 | // 160 | // come to think of it, we can probably just use the length set in *len_p 161 | // once we fetch the result? 162 | result_buffers[2].buffer_type = MYSQL_TYPE_LONG_BLOB; 163 | result_buffers[2].buffer = 0; 164 | result_buffers[2].buffer_length = 0; 165 | result_buffers[2].length = &data_len; 166 | 167 | if(mysql_stmt_bind_result(backend->st_read, result_buffers) != 0) 168 | return GIT_ERROR; 169 | 170 | // this should populate the buffers at *type_p, *len_p and &data_len 171 | error = mysql_stmt_fetch(backend->st_read); 172 | // if(error != 0 || error != MYSQL_DATA_TRUNCATED) 173 | // return GIT_ERROR; 174 | 175 | if (data_len > 0) { 176 | *data_p = malloc(data_len); 177 | result_buffers[2].buffer = *data_p; 178 | result_buffers[2].buffer_length = data_len; 179 | 180 | if (mysql_stmt_fetch_column(backend->st_read, &result_buffers[2], 2, 0) != 0) 181 | return GIT_ERROR; 182 | } 183 | 184 | error = GIT_OK; 185 | } else { 186 | error = GIT_ENOTFOUND; 187 | } 188 | 189 | // reset the statement for further use 190 | if (mysql_stmt_reset(backend->st_read) != 0) 191 | return 0; 192 | 193 | return error; 194 | } 195 | 196 | int mysql_backend__exists(git_odb_backend *_backend, const git_oid *oid) 197 | { 198 | mysql_backend *backend; 199 | int found; 200 | MYSQL_BIND bind_buffers[1]; 201 | 202 | assert(_backend && oid); 203 | 204 | backend = (mysql_backend *)_backend; 205 | found = 0; 206 | 207 | memset(bind_buffers, 0, sizeof(bind_buffers)); 208 | 209 | // bind the oid passed to the statement 210 | bind_buffers[0].buffer = (void*)oid->id; 211 | bind_buffers[0].buffer_length = 20; 212 | bind_buffers[0].length = &bind_buffers[0].buffer_length; 213 | bind_buffers[0].buffer_type = MYSQL_TYPE_BLOB; 214 | if (mysql_stmt_bind_param(backend->st_read_header, bind_buffers) != 0) 215 | return 0; 216 | 217 | // execute the statement 218 | if (mysql_stmt_execute(backend->st_read_header) != 0) 219 | return 0; 220 | 221 | if (mysql_stmt_store_result(backend->st_read_header) != 0) 222 | return 0; 223 | 224 | // now lets see if any rows matched our query 225 | // this should either be 0 or 1 226 | // if it's > 1 MySQL's unique index failed and we should all fear for our lives 227 | if (mysql_stmt_num_rows(backend->st_read_header) == 1) { 228 | found = 1; 229 | } 230 | 231 | // reset the statement for further use 232 | if (mysql_stmt_reset(backend->st_read_header) != 0) 233 | return 0; 234 | 235 | return found; 236 | } 237 | 238 | int mysql_backend__write(git_odb_backend *_backend, const git_oid *oid, const void *data, size_t len, git_otype type) 239 | { 240 | int error; 241 | mysql_backend *backend; 242 | MYSQL_BIND bind_buffers[4]; 243 | my_ulonglong affected_rows; 244 | 245 | assert(oid && _backend && data); 246 | 247 | backend = (mysql_backend *)_backend; 248 | 249 | memset(bind_buffers, 0, sizeof(bind_buffers)); 250 | 251 | // bind the oid 252 | bind_buffers[0].buffer = (void*)oid->id; 253 | bind_buffers[0].buffer_length = 20; 254 | bind_buffers[0].length = &bind_buffers[0].buffer_length; 255 | bind_buffers[0].buffer_type = MYSQL_TYPE_BLOB; 256 | 257 | // bind the type 258 | bind_buffers[1].buffer = &type; 259 | bind_buffers[1].buffer_type = MYSQL_TYPE_TINY; 260 | 261 | // bind the size of the data 262 | bind_buffers[2].buffer = &len; 263 | bind_buffers[2].buffer_type = MYSQL_TYPE_LONG; 264 | 265 | // bind the data 266 | bind_buffers[3].buffer = (void*)data; 267 | bind_buffers[3].buffer_length = len; 268 | bind_buffers[3].length = &bind_buffers[3].buffer_length; 269 | bind_buffers[3].buffer_type = MYSQL_TYPE_BLOB; 270 | 271 | if (mysql_stmt_bind_param(backend->st_write, bind_buffers) != 0) 272 | return GIT_ERROR; 273 | 274 | // TODO: use the streaming backend API so this actually makes sense to use :P 275 | // once we want to use this we should comment out 276 | // if (mysql_stmt_send_long_data(backend->st_write, 2, data, len) != 0) 277 | // return GIT_ERROR; 278 | 279 | // execute the statement 280 | if (mysql_stmt_execute(backend->st_write) != 0) 281 | return GIT_ERROR; 282 | 283 | // now lets see if the insert worked 284 | affected_rows = mysql_stmt_affected_rows(backend->st_write); 285 | if (affected_rows != 1) 286 | return GIT_ERROR; 287 | 288 | // reset the statement for further use 289 | if (mysql_stmt_reset(backend->st_read_header) != 0) 290 | return GIT_ERROR; 291 | 292 | return GIT_OK; 293 | } 294 | 295 | void mysql_backend__free(git_odb_backend *_backend) 296 | { 297 | mysql_backend *backend; 298 | assert(_backend); 299 | backend = (mysql_backend *)_backend; 300 | 301 | if (backend->st_read) 302 | mysql_stmt_close(backend->st_read); 303 | if (backend->st_read_header) 304 | mysql_stmt_close(backend->st_read_header); 305 | if (backend->st_write) 306 | mysql_stmt_close(backend->st_write); 307 | 308 | mysql_close(backend->db); 309 | 310 | free(backend); 311 | } 312 | 313 | static int create_table(MYSQL *db) 314 | { 315 | static const char *sql_create = 316 | "CREATE TABLE `" GIT2_TABLE_NAME "` (" 317 | " `oid` binary(20) NOT NULL DEFAULT ''," 318 | " `type` tinyint(1) unsigned NOT NULL," 319 | " `size` bigint(20) unsigned NOT NULL," 320 | " `data` longblob NOT NULL," 321 | " PRIMARY KEY (`oid`)," 322 | " KEY `type` (`type`)," 323 | " KEY `size` (`size`)" 324 | ") ENGINE=" GIT2_STORAGE_ENGINE " DEFAULT CHARSET=utf8 COLLATE=utf8_bin;"; 325 | 326 | if (mysql_real_query(db, sql_create, strlen(sql_create)) != 0) 327 | return GIT_ERROR; 328 | 329 | return GIT_OK; 330 | } 331 | 332 | static int init_db(MYSQL *db) 333 | { 334 | static const char *sql_check = 335 | "SHOW TABLES LIKE '" GIT2_TABLE_NAME "';"; 336 | 337 | MYSQL_RES *res; 338 | int error; 339 | my_ulonglong num_rows; 340 | 341 | if (mysql_real_query(db, sql_check, strlen(sql_check)) != 0) 342 | return GIT_ERROR; 343 | 344 | res = mysql_store_result(db); 345 | if (res == NULL) 346 | return GIT_ERROR; 347 | 348 | num_rows = mysql_num_rows(res); 349 | if (num_rows == 0) { 350 | /* the table was not found */ 351 | error = create_table(db); 352 | } else if (num_rows > 0) { 353 | /* the table was found */ 354 | error = GIT_OK; 355 | } else { 356 | error = GIT_ERROR; 357 | } 358 | 359 | mysql_free_result(res); 360 | return error; 361 | } 362 | 363 | static int init_statements(mysql_backend *backend) 364 | { 365 | my_bool truth = 1; 366 | 367 | static const char *sql_read = 368 | "SELECT `type`, `size`, UNCOMPRESS(`data`) FROM `" GIT2_TABLE_NAME "` WHERE `oid` = ?;"; 369 | 370 | static const char *sql_read_header = 371 | "SELECT `type`, `size` FROM `" GIT2_TABLE_NAME "` WHERE `oid` = ?;"; 372 | 373 | static const char *sql_write = 374 | "INSERT IGNORE INTO `" GIT2_TABLE_NAME "` VALUES (?, ?, ?, COMPRESS(?));"; 375 | 376 | 377 | backend->st_read = mysql_stmt_init(backend->db); 378 | if (backend->st_read == NULL) 379 | return GIT_ERROR; 380 | 381 | if (mysql_stmt_attr_set(backend->st_read, STMT_ATTR_UPDATE_MAX_LENGTH, &truth) != 0) 382 | return GIT_ERROR; 383 | 384 | if (mysql_stmt_prepare(backend->st_read, sql_read, strlen(sql_read)) != 0) 385 | return GIT_ERROR; 386 | 387 | 388 | backend->st_read_header = mysql_stmt_init(backend->db); 389 | if (backend->st_read_header == NULL) 390 | return GIT_ERROR; 391 | 392 | if (mysql_stmt_attr_set(backend->st_read_header, STMT_ATTR_UPDATE_MAX_LENGTH, &truth) != 0) 393 | return GIT_ERROR; 394 | 395 | if (mysql_stmt_prepare(backend->st_read_header, sql_read_header, strlen(sql_read)) != 0) 396 | return GIT_ERROR; 397 | 398 | 399 | backend->st_write = mysql_stmt_init(backend->db); 400 | if (backend->st_write == NULL) 401 | return GIT_ERROR; 402 | 403 | if (mysql_stmt_attr_set(backend->st_write, STMT_ATTR_UPDATE_MAX_LENGTH, &truth) != 0) 404 | return GIT_ERROR; 405 | 406 | if (mysql_stmt_prepare(backend->st_write, sql_write, strlen(sql_read)) != 0) 407 | return GIT_ERROR; 408 | 409 | 410 | return GIT_OK; 411 | } 412 | 413 | int git_odb_backend_mysql(git_odb_backend **backend_out, const char *mysql_host, 414 | const char *mysql_user, const char *mysql_passwd, const char *mysql_db, 415 | unsigned int mysql_port, const char *mysql_unix_socket, unsigned long mysql_client_flag) 416 | { 417 | mysql_backend *backend; 418 | int error; 419 | my_bool reconnect; 420 | 421 | backend = calloc(1, sizeof(mysql_backend)); 422 | if (backend == NULL) { 423 | giterr_set_oom(); 424 | return GIT_ERROR; 425 | } 426 | 427 | backend->db = mysql_init(backend->db); 428 | 429 | reconnect = 1; 430 | // allow libmysql to reconnect gracefully 431 | if (mysql_options(backend->db, MYSQL_OPT_RECONNECT, &reconnect) != 0) 432 | goto cleanup; 433 | 434 | // make the connection 435 | if (mysql_real_connect(backend->db, mysql_host, mysql_user, mysql_passwd, mysql_db, mysql_port, mysql_unix_socket, mysql_client_flag) != backend->db) 436 | goto cleanup; 437 | 438 | // check for and possibly create the database 439 | error = init_db(backend->db); 440 | if (error < 0) 441 | goto cleanup; 442 | 443 | error = init_statements(backend); 444 | if (error < 0) 445 | goto cleanup; 446 | 447 | backend->parent.version = GIT_ODB_BACKEND_VERSION; 448 | backend->parent.read = &mysql_backend__read; 449 | backend->parent.read_header = &mysql_backend__read_header; 450 | backend->parent.write = &mysql_backend__write; 451 | backend->parent.exists = &mysql_backend__exists; 452 | backend->parent.free = &mysql_backend__free; 453 | 454 | *backend_out = (git_odb_backend *)backend; 455 | return GIT_OK; 456 | 457 | cleanup: 458 | mysql_backend__free((git_odb_backend *)backend); 459 | return GIT_ERROR; 460 | } 461 | -------------------------------------------------------------------------------- /redis/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PROJECT(libgit2-redis C) 2 | CMAKE_MINIMUM_REQUIRED(VERSION 2.6) 3 | 4 | INCLUDE(../CMake/FindLibgit2.cmake) 5 | INCLUDE(../CMake/FindHiredis.cmake) 6 | 7 | # Build options 8 | OPTION (BUILD_SHARED_LIBS "Build Shared Library (OFF for Static)" ON) 9 | OPTION (BUILD_TESTS "Build Tests" ON) 10 | 11 | # Build Release by default 12 | IF (NOT CMAKE_BUILD_TYPE) 13 | SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) 14 | ENDIF () 15 | 16 | # Compile and link libgit2 17 | INCLUDE_DIRECTORIES(${LIBGIT2_INCLUDE_DIR} ${LIBHIREDIS_INCLUDE_DIR}) 18 | 19 | IF (BUILD_SHARED_LIBS) 20 | ADD_LIBRARY(git2-redis SHARED hiredis.c) 21 | ELSE () 22 | ADD_LIBRARY(git2-redis STATIC hiredis.c) 23 | ENDIF () 24 | 25 | TARGET_LINK_LIBRARIES(git2-redis ${LIBGIT2_LIBRARIES} ${LIBHIREDIS_LIBRARIES}) 26 | -------------------------------------------------------------------------------- /redis/hiredis.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License, version 2, 4 | * as published by the Free Software Foundation. 5 | * 6 | * In addition to the permissions in the GNU General Public License, 7 | * the authors give you unlimited permission to link the compiled 8 | * version of this file into combinations with other programs, 9 | * and to distribute those combinations without any restriction 10 | * coming from the use of this file. (The General Public License 11 | * restrictions do apply in other respects; for example, they cover 12 | * modification of the file, and distribution when not linked into 13 | * a combined executable.) 14 | * 15 | * This file is distributed in the hope that it will be useful, but 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | * General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; see the file COPYING. If not, write to 22 | * the Free Software Foundation, 51 Franklin Street, Fifth Floor, 23 | * Boston, MA 02110-1301, USA. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | #include 33 | 34 | typedef struct { 35 | git_odb_backend parent; 36 | 37 | char *prefix; 38 | char *repo_path; 39 | redisContext *db; 40 | } hiredis_odb_backend; 41 | 42 | typedef struct { 43 | git_refdb_backend parent; 44 | 45 | char *prefix; 46 | char *repo_path; 47 | redisContext *db; 48 | } hiredis_refdb_backend; 49 | 50 | typedef struct { 51 | git_reference_iterator parent; 52 | 53 | size_t current; 54 | redisReply *keys; 55 | 56 | hiredis_refdb_backend *backend; 57 | } hiredis_refdb_iterator; 58 | 59 | static redisContext *sharedConnection = NULL; 60 | 61 | /* Odb methods */ 62 | 63 | int hiredis_odb_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *_backend, const git_oid *oid) 64 | { 65 | hiredis_odb_backend *backend; 66 | int error; 67 | redisReply *reply; 68 | char *str_id = calloc(GIT_OID_HEXSZ + 1, sizeof(char)); 69 | 70 | assert(len_p && type_p && _backend && oid); 71 | 72 | backend = (hiredis_odb_backend *) _backend; 73 | error = GIT_ERROR; 74 | 75 | git_oid_tostr(str_id, GIT_OID_HEXSZ, oid); 76 | 77 | reply = redisCommand(backend->db, "HMGET %s:%s:odb:%s %s %s", backend->prefix, backend->repo_path, str_id, "type", "size"); 78 | 79 | if (reply && reply->type == REDIS_REPLY_ARRAY) { 80 | if (reply->element[0]->type != REDIS_REPLY_NIL && 81 | reply->element[0]->type != REDIS_REPLY_NIL) { 82 | *type_p = (git_otype) atoi(reply->element[0]->str); 83 | *len_p = (size_t) atoi(reply->element[1]->str); 84 | error = GIT_OK; 85 | } else { 86 | giterr_set_str(GITERR_ODB, "Redis odb storage corrupted"); 87 | error = GIT_ENOTFOUND; 88 | } 89 | } else { 90 | giterr_set_str(GITERR_ODB, "Redis odb storage error"); 91 | error = GIT_ERROR; 92 | } 93 | 94 | free(str_id); 95 | freeReplyObject(reply); 96 | return error; 97 | } 98 | 99 | int hiredis_odb_backend__read(void **data_p, size_t *len_p, git_otype *type_p, git_odb_backend *_backend, const git_oid *oid) 100 | { 101 | hiredis_odb_backend *backend; 102 | int error; 103 | redisReply *reply; 104 | char *str_id = calloc(GIT_OID_HEXSZ + 1, sizeof(char)); 105 | 106 | assert(data_p && len_p && type_p && _backend && oid); 107 | 108 | backend = (hiredis_odb_backend *) _backend; 109 | error = GIT_ERROR; 110 | 111 | git_oid_tostr(str_id, GIT_OID_HEXSZ, oid); 112 | 113 | reply = redisCommand(backend->db, "HMGET %s:%s:odb:%s %s %s %s", backend->prefix, backend->repo_path, str_id, 114 | "type", "size", "data"); 115 | 116 | if (reply && reply->type == REDIS_REPLY_ARRAY) { 117 | if (reply->element[0]->type != REDIS_REPLY_NIL && 118 | reply->element[1]->type != REDIS_REPLY_NIL && 119 | reply->element[2]->type != REDIS_REPLY_NIL) { 120 | *type_p = (git_otype) atoi(reply->element[0]->str); 121 | *len_p = (size_t) atoi(reply->element[1]->str); 122 | *data_p = malloc(*len_p); 123 | if (*data_p == NULL) { 124 | error = GITERR_NOMEMORY; 125 | } else { 126 | memcpy(*data_p, reply->element[2]->str, *len_p); 127 | error = GIT_OK; 128 | } 129 | } else { 130 | giterr_set_str(GITERR_ODB, "Redis odb couldn't find object"); 131 | error = GIT_ENOTFOUND; 132 | } 133 | } else { 134 | giterr_set_str(GITERR_ODB, "Redis odb storage error"); 135 | error = GIT_ERROR; 136 | } 137 | 138 | free(str_id); 139 | freeReplyObject(reply); 140 | return error; 141 | } 142 | 143 | int hiredis_odb_backend__read_prefix(git_oid *out_oid, 144 | void **data_p, size_t *len_p, git_otype *type_p, git_odb_backend *_backend, 145 | const git_oid *short_oid, size_t len) 146 | { 147 | if (len >= GIT_OID_HEXSZ) { 148 | /* Just match the full identifier */ 149 | int error = hiredis_odb_backend__read(data_p, len_p, type_p, _backend, short_oid); 150 | if (error == GIT_OK) 151 | git_oid_cpy(out_oid, short_oid); 152 | 153 | return error; 154 | } 155 | 156 | /* TODO prefix */ 157 | giterr_set_str(GITERR_ODB, "Redis odb doesn't not implement oid prefix lookup"); 158 | return GITERR_INVALID; 159 | } 160 | 161 | int hiredis_odb_backend__exists(git_odb_backend *_backend, const git_oid *oid) 162 | { 163 | hiredis_odb_backend *backend; 164 | int found; 165 | redisReply *reply; 166 | char *str_id = calloc(GIT_OID_HEXSZ + 1, sizeof(char)); 167 | 168 | assert(_backend && oid); 169 | 170 | backend = (hiredis_odb_backend *) _backend; 171 | found = 0; 172 | 173 | git_oid_tostr(str_id, GIT_OID_HEXSZ, oid); 174 | 175 | reply = redisCommand(backend->db, "exists %s:%s:odb:%s", backend->prefix, backend->repo_path, str_id); 176 | if (reply->type == REDIS_REPLY_INTEGER) 177 | found = reply->integer; 178 | 179 | free(str_id); 180 | freeReplyObject(reply); 181 | return found; 182 | } 183 | 184 | int hiredis_odb_backend__write(git_odb_backend *_backend, const git_oid *oid, const void *data, size_t len, git_otype type) 185 | { 186 | hiredis_odb_backend *backend; 187 | int error; 188 | redisReply *reply; 189 | char *str_id = calloc(GIT_OID_HEXSZ + 1, sizeof(char)); 190 | 191 | assert(oid && _backend && data); 192 | 193 | backend = (hiredis_odb_backend *) _backend; 194 | error = GIT_ERROR; 195 | 196 | git_oid_tostr(str_id, GIT_OID_HEXSZ, oid); 197 | 198 | reply = redisCommand(backend->db, "HMSET %s:%s:odb:%s " 199 | "type %d " 200 | "size %d " 201 | "data %b ", backend->prefix, backend->repo_path, str_id, 202 | (int) type, len, data, len); 203 | free(str_id); 204 | 205 | error = (reply == NULL || reply->type == REDIS_REPLY_ERROR) ? GIT_ERROR : GIT_OK; 206 | 207 | freeReplyObject(reply); 208 | return error; 209 | } 210 | 211 | void hiredis_odb_backend__free(git_odb_backend *_backend) 212 | { 213 | hiredis_odb_backend *backend; 214 | 215 | assert(_backend); 216 | backend = (hiredis_odb_backend *) _backend; 217 | 218 | free(backend->repo_path); 219 | free(backend->prefix); 220 | 221 | redisFree(backend->db); 222 | 223 | free(backend); 224 | } 225 | 226 | /* Refdb methods */ 227 | 228 | int hiredis_refdb_backend__exists(int *exists, git_refdb_backend *_backend, const char *ref_name) 229 | { 230 | hiredis_refdb_backend *backend; 231 | int error = GIT_OK; 232 | redisReply *reply; 233 | 234 | assert(ref_name && _backend); 235 | 236 | backend = (hiredis_refdb_backend *) _backend; 237 | 238 | reply = redisCommand(backend->db, "EXISTS %s:%s:refdb:%s", backend->prefix, backend->repo_path, ref_name); 239 | if (reply->type == REDIS_REPLY_INTEGER) { 240 | *exists = reply->integer; 241 | } else { 242 | giterr_set_str(GITERR_REFERENCE, "Redis refdb storage error"); 243 | error = GIT_ERROR; 244 | } 245 | 246 | freeReplyObject(reply); 247 | return error; 248 | } 249 | 250 | int hiredis_refdb_backend__lookup(git_reference **out, git_refdb_backend *_backend, const char *ref_name) 251 | { 252 | hiredis_refdb_backend *backend; 253 | int error = GIT_OK; 254 | redisReply *reply; 255 | git_oid oid; 256 | 257 | assert(ref_name && _backend); 258 | 259 | backend = (hiredis_refdb_backend *) _backend; 260 | 261 | reply = redisCommand(backend->db, "HMGET %s:%s:refdb:%s type target", backend->prefix, backend->repo_path, ref_name); 262 | if(reply->type == REDIS_REPLY_ARRAY) { 263 | if (reply->element[0]->type != REDIS_REPLY_NIL && reply->element[1]->type != REDIS_REPLY_NIL) { 264 | git_ref_t type = (git_ref_t) atoi(reply->element[0]->str); 265 | 266 | if (type == GIT_REF_OID) { 267 | git_oid_fromstr(&oid, reply->element[1]->str); 268 | *out = git_reference__alloc(ref_name, &oid, NULL); 269 | } else if (type == GIT_REF_SYMBOLIC) { 270 | *out = git_reference__alloc_symbolic(ref_name, reply->element[1]->str); 271 | } else { 272 | giterr_set_str(GITERR_REFERENCE, "Redis refdb storage corrupted (unknown ref type returned)"); 273 | error = GIT_ERROR; 274 | } 275 | 276 | } else { 277 | giterr_set_str(GITERR_REFERENCE, "Redis refdb couldn't find ref"); 278 | error = GIT_ENOTFOUND; 279 | } 280 | } else { 281 | giterr_set_str(GITERR_REFERENCE, "Redis refdb storage error"); 282 | error = GIT_ERROR; 283 | } 284 | 285 | freeReplyObject(reply); 286 | return error; 287 | } 288 | 289 | int hiredis_refdb_backend__iterator_next(git_reference **ref, git_reference_iterator *_iter) { 290 | hiredis_refdb_iterator *iter; 291 | hiredis_refdb_backend *backend; 292 | char* ref_name; 293 | int error; 294 | 295 | assert(_iter); 296 | iter = (hiredis_refdb_iterator *) _iter; 297 | 298 | if(iter->current < iter->keys->elements) { 299 | ref_name = strstr(iter->keys->element[iter->current++]->str, ":refdb:") + 7; 300 | error = hiredis_refdb_backend__lookup(ref, (git_refdb_backend *) iter->backend, ref_name); 301 | 302 | return error; 303 | } else { 304 | return GIT_ITEROVER; 305 | } 306 | } 307 | 308 | int hiredis_refdb_backend__iterator_next_name(const char **ref_name, git_reference_iterator *_iter) { 309 | hiredis_refdb_iterator *iter; 310 | 311 | assert(_iter); 312 | iter = (hiredis_refdb_iterator *) _iter; 313 | 314 | if(iter->current < iter->keys->elements) { 315 | *ref_name = strdup(strstr(iter->keys->element[iter->current++]->str, ":refdb:") + 7); 316 | 317 | return GIT_OK; 318 | } else { 319 | return GIT_ITEROVER; 320 | } 321 | } 322 | 323 | void hiredis_refdb_backend__iterator_free(git_reference_iterator *_iter) { 324 | hiredis_refdb_iterator *iter; 325 | 326 | assert(_iter); 327 | iter = (hiredis_refdb_iterator *) _iter; 328 | 329 | freeReplyObject(iter->keys); 330 | 331 | free(iter); 332 | } 333 | 334 | int hiredis_refdb_backend__iterator(git_reference_iterator **_iter, struct git_refdb_backend *_backend, const char *glob) 335 | { 336 | hiredis_refdb_backend *backend; 337 | hiredis_refdb_iterator *iterator; 338 | int error = GIT_OK; 339 | redisReply *reply; 340 | 341 | assert(_backend); 342 | 343 | backend = (hiredis_refdb_backend *) _backend; 344 | 345 | reply = redisCommand(backend->db, "KEYS %s:%s:refdb:%s", backend->prefix, backend->repo_path, (glob != NULL ? glob : "refs/*")); 346 | if(reply->type != REDIS_REPLY_ARRAY) { 347 | freeReplyObject(reply); 348 | giterr_set_str(GITERR_REFERENCE, "Redis refdb storage error"); 349 | return GIT_ERROR; 350 | } 351 | 352 | iterator = calloc(1, sizeof(hiredis_refdb_iterator)); 353 | 354 | iterator->backend = backend; 355 | iterator->keys = reply; 356 | 357 | iterator->parent.next = &hiredis_refdb_backend__iterator_next; 358 | iterator->parent.next_name = &hiredis_refdb_backend__iterator_next_name; 359 | iterator->parent.free = &hiredis_refdb_backend__iterator_free; 360 | 361 | *_iter = (git_reference_iterator *) iterator; 362 | 363 | return GIT_OK; 364 | } 365 | 366 | int hiredis_refdb_backend__write(git_refdb_backend *_backend, const git_reference *ref, int force, const git_signature *who, 367 | const char *message, const git_oid *old, const char *old_target) 368 | { 369 | hiredis_refdb_backend *backend; 370 | int error = GIT_OK; 371 | redisReply *reply; 372 | 373 | const char *name = git_reference_name(ref); 374 | const git_oid *target; 375 | const char *symbolic_target; 376 | char oid_str[GIT_OID_HEXSZ + 1]; 377 | 378 | assert(ref && _backend); 379 | 380 | backend = (hiredis_refdb_backend *) _backend; 381 | 382 | target = git_reference_target(ref); 383 | symbolic_target = git_reference_symbolic_target(ref); 384 | 385 | /* FIXME handle force correctly */ 386 | 387 | if (target) { 388 | git_oid_nfmt(oid_str, sizeof(oid_str), target); 389 | reply = redisCommand(backend->db, "HMSET %s:%s:refdb:%s type %d target %s", backend->prefix, backend->repo_path, name, GIT_REF_OID, oid_str); 390 | } else { 391 | symbolic_target = git_reference_symbolic_target(ref); 392 | reply = redisCommand(backend->db, "HMSET %s:%s:refdb:%s type %d target %s", backend->prefix, backend->repo_path, name, GIT_REF_SYMBOLIC, symbolic_target); 393 | } 394 | 395 | if(reply->type == REDIS_REPLY_ERROR) { 396 | giterr_set_str(GITERR_REFERENCE, "Redis refdb storage error"); 397 | error = GIT_ERROR; 398 | } 399 | 400 | freeReplyObject(reply); 401 | return error; 402 | } 403 | 404 | int hiredis_refdb_backend__rename(git_reference **out, git_refdb_backend *_backend, const char *old_name, 405 | const char *new_name, int force, const git_signature *who, const char *message) 406 | { 407 | hiredis_refdb_backend *backend; 408 | int error = GIT_OK; 409 | redisReply *reply; 410 | 411 | assert(old_name && new_name && _backend); 412 | 413 | backend = (hiredis_refdb_backend *) _backend; 414 | 415 | reply = redisCommand(backend->db, "RENAME %s:%s:refdb:%s %s:%s:refdb:%s", 416 | backend->prefix, backend->repo_path, old_name, backend->prefix, backend->repo_path, new_name); 417 | if(reply->type == REDIS_REPLY_ERROR) { 418 | freeReplyObject(reply); 419 | 420 | giterr_set_str(GITERR_REFERENCE, "Redis refdb storage error"); 421 | return GIT_ERROR; 422 | } 423 | 424 | freeReplyObject(reply); 425 | return hiredis_refdb_backend__lookup(out, _backend, new_name); 426 | } 427 | 428 | int hiredis_refdb_backend__del(git_refdb_backend *_backend, const char *ref_name, const git_oid *old, const char *old_target) 429 | { 430 | hiredis_refdb_backend *backend; 431 | int error = GIT_OK; 432 | redisReply *reply; 433 | 434 | assert(ref_name && _backend); 435 | 436 | backend = (hiredis_refdb_backend *) _backend; 437 | 438 | reply = redisCommand(backend->db, "DEL %s:%s:refdb:%s", backend->prefix, backend->repo_path, ref_name); 439 | if(reply->type == REDIS_REPLY_ERROR) { 440 | giterr_set_str(GITERR_REFERENCE, "Redis refdb storage error"); 441 | error = GIT_ERROR; 442 | } 443 | 444 | freeReplyObject(reply); 445 | return error; 446 | } 447 | 448 | void hiredis_refdb_backend__free(git_refdb_backend *_backend) 449 | { 450 | hiredis_refdb_backend *backend; 451 | 452 | assert(_backend); 453 | backend = (hiredis_refdb_backend *) _backend; 454 | 455 | free(backend->repo_path); 456 | free(backend->prefix); 457 | 458 | redisFree(backend->db); 459 | 460 | free(backend); 461 | } 462 | 463 | /* reflog methods */ 464 | 465 | int hiredis_refdb_backend__has_log(git_refdb_backend *_backend, const char *refname) 466 | { 467 | return 0; 468 | } 469 | 470 | int hiredis_refdb_backend__ensure_log(git_refdb_backend *_backend, const char *refname) 471 | { 472 | return GIT_ERROR; 473 | } 474 | 475 | int hiredis_refdb_backend__reflog_read(git_reflog **out, git_refdb_backend *_backend, const char *name) 476 | { 477 | return GIT_ERROR; 478 | } 479 | 480 | int hiredis_refdb_backend__reflog_write(git_refdb_backend *_backend, git_reflog *reflog) 481 | { 482 | return GIT_ERROR; 483 | } 484 | 485 | int hiredis_refdb_backend__reflog_rename(git_refdb_backend *_backend, const char *old_name, const char *new_name) 486 | { 487 | return GIT_ERROR; 488 | } 489 | 490 | int hiredis_refdb_backend__reflog_delete(git_refdb_backend *_backend, const char *name) 491 | { 492 | return GIT_ERROR; 493 | } 494 | 495 | /* Constructors */ 496 | 497 | int git_odb_backend_hiredis(git_odb_backend **backend_out, const char* prefix, const char* path, const char *host, int port, char* password) 498 | { 499 | hiredis_odb_backend *backend; 500 | redisReply *reply; 501 | 502 | backend = calloc(1, sizeof (hiredis_odb_backend)); 503 | if (backend == NULL) 504 | return GITERR_NOMEMORY; 505 | 506 | if (sharedConnection == NULL) { 507 | sharedConnection = redisConnect(host, port); 508 | if (sharedConnection->err) { 509 | free(backend); 510 | giterr_set_str(GITERR_REFERENCE, "Redis odb storage couldn't connect to redis server"); 511 | return GIT_ERROR; 512 | } 513 | 514 | if(password != NULL) { 515 | reply = redisCommand(sharedConnection, "AUTH %s", password); 516 | if (reply->type == REDIS_REPLY_ERROR) { 517 | giterr_set_str(GITERR_REFERENCE, "Redis odb storage authentication with redis server failed"); 518 | return GIT_ERROR; 519 | } 520 | freeReplyObject(reply); 521 | } 522 | } 523 | 524 | backend->db = sharedConnection; 525 | 526 | backend->prefix = strdup(prefix); 527 | backend->repo_path = strdup(path); 528 | 529 | backend->parent.version = 1; 530 | 531 | backend->parent.read = &hiredis_odb_backend__read; 532 | backend->parent.write = &hiredis_odb_backend__write; 533 | backend->parent.read_prefix = &hiredis_odb_backend__read_prefix; 534 | backend->parent.read_header = &hiredis_odb_backend__read_header; 535 | backend->parent.exists = &hiredis_odb_backend__exists; 536 | backend->parent.free = &hiredis_odb_backend__free; 537 | 538 | backend->parent.writestream = NULL; 539 | backend->parent.foreach = NULL; 540 | 541 | *backend_out = (git_odb_backend *) backend; 542 | 543 | return GIT_OK; 544 | } 545 | 546 | int git_refdb_backend_hiredis(git_refdb_backend **backend_out, const char* prefix, const char* path, const char *host, int port, char* password) 547 | { 548 | hiredis_refdb_backend *backend; 549 | redisReply *reply; 550 | 551 | backend = calloc(1, sizeof(hiredis_refdb_backend)); 552 | if (backend == NULL) 553 | return GITERR_NOMEMORY; 554 | 555 | if (sharedConnection == NULL) { 556 | sharedConnection = redisConnect(host, port); 557 | if (sharedConnection->err) { 558 | free(backend); 559 | giterr_set_str(GITERR_REFERENCE, "Redis refdb storage couldn't connect to redis server"); 560 | return GIT_ERROR; 561 | } 562 | 563 | if(password != NULL) { 564 | reply = redisCommand(sharedConnection, "AUTH %s", password); 565 | if (reply->type == REDIS_REPLY_ERROR) { 566 | giterr_set_str(GITERR_REFERENCE, "Redis refdb storage authentication with redis server failed"); 567 | return GIT_ERROR; 568 | } 569 | freeReplyObject(reply); 570 | } 571 | } 572 | 573 | backend->db = sharedConnection; 574 | 575 | backend->prefix = strdup(prefix); 576 | backend->repo_path = strdup(path); 577 | 578 | backend->parent.exists = &hiredis_refdb_backend__exists; 579 | backend->parent.lookup = &hiredis_refdb_backend__lookup; 580 | backend->parent.iterator = &hiredis_refdb_backend__iterator; 581 | backend->parent.write = &hiredis_refdb_backend__write; 582 | backend->parent.del = &hiredis_refdb_backend__del; 583 | backend->parent.rename = &hiredis_refdb_backend__rename; 584 | backend->parent.compress = NULL; 585 | backend->parent.free = &hiredis_refdb_backend__free; 586 | 587 | backend->parent.has_log = &hiredis_refdb_backend__has_log; 588 | backend->parent.ensure_log = &hiredis_refdb_backend__ensure_log; 589 | backend->parent.reflog_read = &hiredis_refdb_backend__reflog_read; 590 | backend->parent.reflog_write = &hiredis_refdb_backend__reflog_write; 591 | backend->parent.reflog_rename = &hiredis_refdb_backend__reflog_rename; 592 | backend->parent.reflog_delete = &hiredis_refdb_backend__reflog_delete; 593 | 594 | *backend_out = (git_refdb_backend *) backend; 595 | 596 | return GIT_OK; 597 | } 598 | 599 | -------------------------------------------------------------------------------- /sqlite/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | PROJECT(LIBGIT2-sqlite C) 2 | CMAKE_MINIMUM_REQUIRED(VERSION 2.6) 3 | 4 | INCLUDE(../CMake/FindLibgit2.cmake) 5 | INCLUDE(../CMake/FindSQLite3.cmake) 6 | 7 | # Build options 8 | OPTION (BUILD_SHARED_LIBS "Build Shared Library (OFF for Static)" ON) 9 | OPTION (BUILD_TESTS "Build Tests" ON) 10 | 11 | # Build Release by default 12 | IF (NOT CMAKE_BUILD_TYPE) 13 | SET(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: Debug Release RelWithDebInfo MinSizeRel." FORCE) 14 | ENDIF () 15 | 16 | # Compile and link LIBGIT2 17 | INCLUDE_DIRECTORIES(${LIBGIT2_INCLUDE_DIRS} ${SQLITE3_INCLUDE_DIRS}) 18 | ADD_LIBRARY(git2-sqlite sqlite.c) 19 | TARGET_LINK_LIBRARIES(git2-sqlite ${LIBGIT2_LIBRARIES} ${SQLITE3_LIBRARIES}) 20 | -------------------------------------------------------------------------------- /sqlite/sqlite.c: -------------------------------------------------------------------------------- 1 | /* 2 | * This file is free software; you can redistribute it and/or modify 3 | * it under the terms of the GNU General Public License, version 2, 4 | * as published by the Free Software Foundation. 5 | * 6 | * In addition to the permissions in the GNU General Public License, 7 | * the authors give you unlimited permission to link the compiled 8 | * version of this file into combinations with other programs, 9 | * and to distribute those combinations without any restriction 10 | * coming from the use of this file. (The General Public License 11 | * restrictions do apply in other respects; for example, they cover 12 | * modification of the file, and distribution when not linked into 13 | * a combined executable.) 14 | * 15 | * This file is distributed in the hope that it will be useful, but 16 | * WITHOUT ANY WARRANTY; without even the implied warranty of 17 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 18 | * General Public License for more details. 19 | * 20 | * You should have received a copy of the GNU General Public License 21 | * along with this program; see the file COPYING. If not, write to 22 | * the Free Software Foundation, 51 Franklin Street, Fifth Floor, 23 | * Boston, MA 02110-1301, USA. 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #define GIT2_TABLE_NAME "git2_odb" 33 | 34 | typedef struct { 35 | git_odb_backend parent; 36 | sqlite3 *db; 37 | sqlite3_stmt *st_read; 38 | sqlite3_stmt *st_write; 39 | sqlite3_stmt *st_read_header; 40 | } sqlite_backend; 41 | 42 | int sqlite_backend__read_header(size_t *len_p, git_otype *type_p, git_odb_backend *_backend, const git_oid *oid) 43 | { 44 | sqlite_backend *backend; 45 | int error; 46 | 47 | assert(len_p && type_p && _backend && oid); 48 | 49 | backend = (sqlite_backend *)_backend; 50 | error = GIT_ERROR; 51 | 52 | if (sqlite3_bind_text(backend->st_read_header, 1, (char *)oid->id, 20, SQLITE_TRANSIENT) == SQLITE_OK) { 53 | if (sqlite3_step(backend->st_read_header) == SQLITE_ROW) { 54 | *type_p = (git_otype)sqlite3_column_int(backend->st_read_header, 0); 55 | *len_p = (size_t)sqlite3_column_int(backend->st_read_header, 1); 56 | assert(sqlite3_step(backend->st_read_header) == SQLITE_DONE); 57 | error = GIT_OK; 58 | } else { 59 | error = GIT_ENOTFOUND; 60 | } 61 | } 62 | 63 | sqlite3_reset(backend->st_read_header); 64 | return error; 65 | } 66 | 67 | int sqlite_backend__read(void **data_p, size_t *len_p, git_otype *type_p, git_odb_backend *_backend, const git_oid *oid) 68 | { 69 | sqlite_backend *backend; 70 | int error; 71 | 72 | assert(data_p && len_p && type_p && _backend && oid); 73 | 74 | backend = (sqlite_backend *)_backend; 75 | error = GIT_ERROR; 76 | 77 | if (sqlite3_bind_text(backend->st_read, 1, (char *)oid->id, 20, SQLITE_TRANSIENT) == SQLITE_OK) { 78 | if (sqlite3_step(backend->st_read) == SQLITE_ROW) { 79 | *type_p = (git_otype)sqlite3_column_int(backend->st_read, 0); 80 | *len_p = (size_t)sqlite3_column_int(backend->st_read, 1); 81 | *data_p = malloc(*len_p); 82 | 83 | if (*data_p == NULL) { 84 | giterr_set_oom(); 85 | error = GIT_ERROR; 86 | } else { 87 | memcpy(*data_p, sqlite3_column_blob(backend->st_read, 2), *len_p); 88 | error = GIT_OK; 89 | } 90 | 91 | assert(sqlite3_step(backend->st_read) == SQLITE_DONE); 92 | } else { 93 | error = GIT_ENOTFOUND; 94 | } 95 | } 96 | 97 | sqlite3_reset(backend->st_read); 98 | return error; 99 | } 100 | 101 | int sqlite_backend__read_prefix(git_oid *out_oid, void **data_p, size_t *len_p, git_otype *type_p, git_odb_backend *_backend, 102 | const git_oid *short_oid, size_t len) { 103 | if (len >= GIT_OID_HEXSZ) { 104 | /* Just match the full identifier */ 105 | int error = sqlite_backend__read(data_p, len_p, type_p, _backend, short_oid); 106 | if (error == GIT_OK) 107 | git_oid_cpy(out_oid, short_oid); 108 | 109 | return error; 110 | } 111 | /* not implemented (yet) */ 112 | return GIT_ERROR; 113 | } 114 | 115 | int sqlite_backend__exists(git_odb_backend *_backend, const git_oid *oid) 116 | { 117 | sqlite_backend *backend; 118 | int found; 119 | 120 | assert(_backend && oid); 121 | 122 | backend = (sqlite_backend *)_backend; 123 | found = 0; 124 | 125 | if (sqlite3_bind_text(backend->st_read_header, 1, (char *)oid->id, 20, SQLITE_TRANSIENT) == SQLITE_OK) { 126 | if (sqlite3_step(backend->st_read_header) == SQLITE_ROW) { 127 | found = 1; 128 | assert(sqlite3_step(backend->st_read_header) == SQLITE_DONE); 129 | } 130 | } 131 | 132 | sqlite3_reset(backend->st_read_header); 133 | return found; 134 | } 135 | 136 | 137 | int sqlite_backend__write(git_odb_backend *_backend, const git_oid *id, const void *data, size_t len, git_otype type) 138 | { 139 | int error; 140 | sqlite_backend *backend; 141 | 142 | assert(id && _backend && data); 143 | 144 | backend = (sqlite_backend *)_backend; 145 | 146 | error = SQLITE_ERROR; 147 | 148 | if (sqlite3_bind_text(backend->st_write, 1, (char *)id->id, 20, SQLITE_TRANSIENT) == SQLITE_OK && 149 | sqlite3_bind_int(backend->st_write, 2, (int)type) == SQLITE_OK && 150 | sqlite3_bind_int(backend->st_write, 3, len) == SQLITE_OK && 151 | sqlite3_bind_blob(backend->st_write, 4, data, len, SQLITE_TRANSIENT) == SQLITE_OK) { 152 | error = sqlite3_step(backend->st_write); 153 | } 154 | 155 | sqlite3_reset(backend->st_write); 156 | return (error == SQLITE_DONE) ? GIT_OK : GIT_ERROR; 157 | } 158 | 159 | 160 | void sqlite_backend__free(git_odb_backend *_backend) 161 | { 162 | sqlite_backend *backend; 163 | assert(_backend); 164 | backend = (sqlite_backend *)_backend; 165 | 166 | sqlite3_finalize(backend->st_read); 167 | sqlite3_finalize(backend->st_read_header); 168 | sqlite3_finalize(backend->st_write); 169 | sqlite3_close(backend->db); 170 | 171 | free(backend); 172 | } 173 | 174 | static int create_table(sqlite3 *db) 175 | { 176 | static const char *sql_creat = 177 | "CREATE TABLE '" GIT2_TABLE_NAME "' (" 178 | "'oid' CHARACTER(20) PRIMARY KEY NOT NULL," 179 | "'type' INTEGER NOT NULL," 180 | "'size' INTEGER NOT NULL," 181 | "'data' BLOB);"; 182 | 183 | if (sqlite3_exec(db, sql_creat, NULL, NULL, NULL) != SQLITE_OK) 184 | return GIT_ERROR; 185 | 186 | return GIT_OK; 187 | } 188 | 189 | static int init_db(sqlite3 *db) 190 | { 191 | static const char *sql_check = 192 | "SELECT name FROM sqlite_master WHERE type='table' AND name='" GIT2_TABLE_NAME "';"; 193 | 194 | sqlite3_stmt *st_check; 195 | int error; 196 | 197 | if (sqlite3_prepare_v2(db, sql_check, -1, &st_check, NULL) != SQLITE_OK) 198 | return GIT_ERROR; 199 | 200 | switch (sqlite3_step(st_check)) { 201 | case SQLITE_DONE: 202 | /* the table was not found */ 203 | error = create_table(db); 204 | break; 205 | 206 | case SQLITE_ROW: 207 | /* the table was found */ 208 | error = GIT_OK; 209 | break; 210 | 211 | default: 212 | error = GIT_ERROR; 213 | break; 214 | } 215 | 216 | sqlite3_finalize(st_check); 217 | return error; 218 | } 219 | 220 | static int init_statements(sqlite_backend *backend) 221 | { 222 | static const char *sql_read = 223 | "SELECT type, size, data FROM '" GIT2_TABLE_NAME "' WHERE oid = ?;"; 224 | 225 | static const char *sql_read_header = 226 | "SELECT type, size FROM '" GIT2_TABLE_NAME "' WHERE oid = ?;"; 227 | 228 | static const char *sql_write = 229 | "INSERT OR IGNORE INTO '" GIT2_TABLE_NAME "' VALUES (?, ?, ?, ?);"; 230 | 231 | if (sqlite3_prepare_v2(backend->db, sql_read, -1, &backend->st_read, NULL) != SQLITE_OK) 232 | return GIT_ERROR; 233 | 234 | if (sqlite3_prepare_v2(backend->db, sql_read_header, -1, &backend->st_read_header, NULL) != SQLITE_OK) 235 | return GIT_ERROR; 236 | 237 | if (sqlite3_prepare_v2(backend->db, sql_write, -1, &backend->st_write, NULL) != SQLITE_OK) 238 | return GIT_ERROR; 239 | 240 | return GIT_OK; 241 | } 242 | 243 | int git_odb_backend_sqlite(git_odb_backend **backend_out, const char *sqlite_db) 244 | { 245 | sqlite_backend *backend; 246 | int error; 247 | 248 | backend = calloc(1, sizeof(sqlite_backend)); 249 | if (backend == NULL) { 250 | giterr_set_oom(); 251 | return GIT_ERROR; 252 | } 253 | 254 | if (sqlite3_open(sqlite_db, &backend->db) != SQLITE_OK) 255 | goto cleanup; 256 | 257 | error = init_db(backend->db); 258 | if (error < 0) 259 | goto cleanup; 260 | 261 | error = init_statements(backend); 262 | if (error < 0) 263 | goto cleanup; 264 | 265 | backend->parent.version = GIT_ODB_BACKEND_VERSION; 266 | backend->parent.read = &sqlite_backend__read; 267 | backend->parent.read_prefix = &sqlite_backend__read_prefix; 268 | backend->parent.read_header = &sqlite_backend__read_header; 269 | backend->parent.write = &sqlite_backend__write; 270 | backend->parent.exists = &sqlite_backend__exists; 271 | backend->parent.free = &sqlite_backend__free; 272 | 273 | *backend_out = (git_odb_backend *)backend; 274 | return GIT_OK; 275 | 276 | cleanup: 277 | sqlite_backend__free((git_odb_backend *)backend); 278 | return error; 279 | } 280 | --------------------------------------------------------------------------------