summaryrefslogtreecommitdiff
path: root/src/backends/sqlite.c
diff options
context:
space:
mode:
authorVicent Marti <tanoku@gmail.com>2011-02-05 19:45:57 +0200
committerVicent Marti <tanoku@gmail.com>2011-02-05 19:45:57 +0200
commitc041af95a2f382b167c59d739323bd84cbdda235 (patch)
treeb75ae5c1f4363352e37bd03d57f7b5b604b9a9dd /src/backends/sqlite.c
parent95901128b88ab1a784e39c5a87328d602af23074 (diff)
downloadlibgit2-c041af95a2f382b167c59d739323bd84cbdda235.tar.gz
Add support for SQLite backends
Configure again the build system to look for SQLite3. If the library is found, the SQLite backend will be automatically compiled. Enjoy *very* fast reads and writes. MASTER PROTIP: Initialize the backend with ":memory" as the path to the SQLite database for fully-hosted in-memory repositories. Rejoice. Signed-off-by: Vicent Marti <tanoku@gmail.com>
Diffstat (limited to 'src/backends/sqlite.c')
-rw-r--r--src/backends/sqlite.c277
1 files changed, 277 insertions, 0 deletions
diff --git a/src/backends/sqlite.c b/src/backends/sqlite.c
new file mode 100644
index 000000000..ad5b679f9
--- /dev/null
+++ b/src/backends/sqlite.c
@@ -0,0 +1,277 @@
+/*
+ * This file is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License, version 2,
+ * as published by the Free Software Foundation.
+ *
+ * In addition to the permissions in the GNU General Public License,
+ * the authors give you unlimited permission to link the compiled
+ * version of this file into combinations with other programs,
+ * and to distribute those combinations without any restriction
+ * coming from the use of this file. (The General Public License
+ * restrictions do apply in other respects; for example, they cover
+ * modification of the file, and distribution when not linked into
+ * a combined executable.)
+ *
+ * This file is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "common.h"
+#include "git2/object.h"
+#include "hash.h"
+#include "odb.h"
+
+#include "git2/odb_backend.h"
+
+#ifdef GIT2_SQLITE_BACKEND
+
+#include <sqlite3.h>
+
+#define GIT2_TABLE_NAME "git2_odb"
+
+typedef struct {
+ git_odb_backend parent;
+ sqlite3 *db;
+ sqlite3_stmt *st_read;
+ sqlite3_stmt *st_write;
+ sqlite3_stmt *st_read_header;
+} sqlite_backend;
+
+int sqlite_backend__read_header(git_rawobj *obj, git_odb_backend *_backend, const git_oid *oid)
+{
+ sqlite_backend *backend;
+ int error;
+
+ assert(obj && _backend && oid);
+
+ backend = (sqlite_backend *)_backend;
+ error = GIT_ERROR;
+ obj->data = NULL;
+
+ if (sqlite3_bind_text(backend->st_read_header, 1, (char *)oid->id, 20, SQLITE_TRANSIENT) == SQLITE_OK) {
+ if (sqlite3_step(backend->st_read_header) == SQLITE_ROW) {
+ obj->type = sqlite3_column_int(backend->st_read_header, 0);
+ obj->len = sqlite3_column_int(backend->st_read_header, 1);
+ assert(sqlite3_step(backend->st_read_header) == SQLITE_DONE);
+ error = GIT_SUCCESS;
+ } else {
+ error = GIT_ENOTFOUND;
+ }
+ }
+
+ sqlite3_reset(backend->st_read_header);
+ return error;
+}
+
+
+int sqlite_backend__read(git_rawobj *obj, git_odb_backend *_backend, const git_oid *oid)
+{
+ sqlite_backend *backend;
+ int error;
+
+ assert(obj && _backend && oid);
+
+ backend = (sqlite_backend *)_backend;
+ error = GIT_ERROR;
+
+ if (sqlite3_bind_text(backend->st_read, 1, (char *)oid->id, 20, SQLITE_TRANSIENT) == SQLITE_OK) {
+ if (sqlite3_step(backend->st_read) == SQLITE_ROW) {
+ obj->type = sqlite3_column_int(backend->st_read, 0);
+ obj->len = sqlite3_column_int(backend->st_read, 1);
+ obj->data = git__malloc(obj->len);
+
+ if (obj->data == NULL) {
+ error = GIT_ENOMEM;
+ } else {
+ memcpy(obj->data, sqlite3_column_blob(backend->st_read, 2), obj->len);
+ error = GIT_SUCCESS;
+ }
+
+ assert(sqlite3_step(backend->st_read) == SQLITE_DONE);
+ } else {
+ error = GIT_ENOTFOUND;
+ }
+ }
+
+ sqlite3_reset(backend->st_read);
+ return error;
+}
+
+int sqlite_backend__exists(git_odb_backend *_backend, const git_oid *oid)
+{
+ sqlite_backend *backend;
+ int found;
+
+ assert(_backend && oid);
+
+ backend = (sqlite_backend *)_backend;
+ found = 0;
+
+ if (sqlite3_bind_text(backend->st_read_header, 1, (char *)oid->id, 20, SQLITE_TRANSIENT) == SQLITE_OK) {
+ if (sqlite3_step(backend->st_read_header) == SQLITE_ROW) {
+ found = 1;
+ assert(sqlite3_step(backend->st_read_header) == SQLITE_DONE);
+ }
+ }
+
+ sqlite3_reset(backend->st_read_header);
+ return found;
+}
+
+
+int sqlite_backend__write(git_oid *id, git_odb_backend *_backend, git_rawobj *obj)
+{
+ char hdr[64];
+ int hdrlen;
+
+ int error;
+ sqlite_backend *backend;
+
+ assert(id && _backend && obj);
+
+ backend = (sqlite_backend *)_backend;
+
+ if ((error = git_odb__hash_obj(id, hdr, sizeof(hdr), &hdrlen, obj)) < 0)
+ return error;
+
+ error = SQLITE_ERROR;
+
+ if (sqlite3_bind_text(backend->st_write, 1, (char *)id->id, 20, SQLITE_TRANSIENT) == SQLITE_OK &&
+ sqlite3_bind_int(backend->st_write, 2, (int)obj->type) == SQLITE_OK &&
+ sqlite3_bind_int(backend->st_write, 3, obj->len) == SQLITE_OK &&
+ sqlite3_bind_blob(backend->st_write, 4, obj->data, obj->len, SQLITE_TRANSIENT) == SQLITE_OK) {
+ error = sqlite3_step(backend->st_write);
+ }
+
+ sqlite3_reset(backend->st_write);
+ return (error == SQLITE_DONE) ? GIT_SUCCESS : GIT_ERROR;
+}
+
+
+void sqlite_backend__free(git_odb_backend *_backend)
+{
+ sqlite_backend *backend;
+ assert(_backend);
+ backend = (sqlite_backend *)_backend;
+
+ sqlite3_finalize(backend->st_read);
+ sqlite3_finalize(backend->st_read_header);
+ sqlite3_finalize(backend->st_write);
+ sqlite3_close(backend->db);
+
+ free(backend);
+}
+
+static int create_table(sqlite3 *db)
+{
+ static const char *sql_creat =
+ "CREATE TABLE '" GIT2_TABLE_NAME "' ("
+ "'oid' CHARACTER(20) PRIMARY KEY NOT NULL,"
+ "'type' INTEGER NOT NULL,"
+ "'size' INTEGER NOT NULL,"
+ "'data' BLOB);";
+
+ if (sqlite3_exec(db, sql_creat, NULL, NULL, NULL) != SQLITE_OK)
+ return GIT_ERROR;
+
+ return GIT_SUCCESS;
+}
+
+static int init_db(sqlite3 *db)
+{
+ static const char *sql_check =
+ "SELECT name FROM sqlite_master WHERE type='table' AND name='" GIT2_TABLE_NAME "';";
+
+ sqlite3_stmt *st_check;
+ int error;
+
+ if (sqlite3_prepare_v2(db, sql_check, -1, &st_check, NULL) != SQLITE_OK)
+ return GIT_ERROR;
+
+ switch (sqlite3_step(st_check)) {
+ case SQLITE_DONE:
+ /* the table was not found */
+ error = create_table(db);
+ break;
+
+ case SQLITE_ROW:
+ /* the table was found */
+ error = GIT_SUCCESS;
+ break;
+
+ default:
+ error = GIT_ERROR;
+ break;
+ }
+
+ sqlite3_finalize(st_check);
+ return error;
+}
+
+static int init_statements(sqlite_backend *backend)
+{
+ static const char *sql_read =
+ "SELECT type, size, data FROM '" GIT2_TABLE_NAME "' WHERE oid = ?;";
+
+ static const char *sql_read_header =
+ "SELECT type, size FROM '" GIT2_TABLE_NAME "' WHERE oid = ?;";
+
+ static const char *sql_write =
+ "INSERT OR IGNORE INTO '" GIT2_TABLE_NAME "' VALUES (?, ?, ?, ?);";
+
+ if (sqlite3_prepare_v2(backend->db, sql_read, -1, &backend->st_read, NULL) != SQLITE_OK)
+ return GIT_ERROR;
+
+ if (sqlite3_prepare_v2(backend->db, sql_read_header, -1, &backend->st_read_header, NULL) != SQLITE_OK)
+ return GIT_ERROR;
+
+ if (sqlite3_prepare_v2(backend->db, sql_write, -1, &backend->st_write, NULL) != SQLITE_OK)
+ return GIT_ERROR;
+
+ return GIT_SUCCESS;
+}
+
+int git_odb_backend_sqlite(git_odb_backend **backend_out, const char *sqlite_db)
+{
+ sqlite_backend *backend;
+ int error;
+
+ backend = git__calloc(1, sizeof(sqlite_backend));
+ if (backend == NULL)
+ return GIT_ENOMEM;
+
+ if (sqlite3_open(sqlite_db, &backend->db) != SQLITE_OK)
+ goto cleanup;
+
+ error = init_db(backend->db);
+ if (error < 0)
+ goto cleanup;
+
+ error = init_statements(backend);
+ if (error < 0)
+ goto cleanup;
+
+ backend->parent.read = &sqlite_backend__read;
+ backend->parent.read_header = &sqlite_backend__read_header;
+ backend->parent.write = &sqlite_backend__write;
+ backend->parent.exists = &sqlite_backend__exists;
+ backend->parent.free = &sqlite_backend__free;
+
+ backend->parent.priority = 0;
+
+ *backend_out = (git_odb_backend *)backend;
+ return GIT_SUCCESS;
+
+cleanup:
+ sqlite_backend__free((git_odb_backend *)backend);
+ return GIT_ERROR;
+}
+
+#endif /* HAVE_SQLITE3 */