=== subversion/libsvn_fs_util/revprop-sqlite.c ================================================================== --- subversion/libsvn_fs_util/revprop-sqlite.c (revision 25329) +++ subversion/libsvn_fs_util/revprop-sqlite.c (patch - level 1) @@ -0,0 +1,245 @@ +/* revprop-sqlite.c + * + * ==================================================================== + * Copyright (c) 2007 CollabNet. All rights reserved. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://subversion.tigris.org/license-1.html. + * If newer versions of this license are posted there, you may use a + * newer version instead, at your option. + * + * This software consists of voluntary contributions made by many + * individuals. For exact contribution history, see the revision + * history and logs, available at http://subversion.tigris.org/. + * ==================================================================== + */ + +#include + +#include "svn_fs.h" +#include "svn_path.h" + +#include "private/svn_fs_revprop.h" +#include "../libsvn_fs/fs-loader.h" +#include "svn_private_config.h" + +/* This is a macro implementation of svn_fs_revision_root_revision(), which + we cannot call from here, because it would create a circular dependency. */ +#define REV_ROOT_REV(root) \ + ((root)->is_txn_root? SVN_INVALID_REVNUM : (root)->rev) + +/* SQLITE->SVN quick error wrap, much like SVN_ERR. */ +#define SQLITE_ERR(x, db) do \ +{ \ + if ((x) != SQLITE_OK) \ + return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL, \ + sqlite3_errmsg((db))); \ +} while (0) + +#ifdef SQLITE3_DEBUG +/* An sqlite query execution callback. */ +static void +sqlite_tracer(void *data, const char *sql) +{ + /* sqlite3 *db = data; */ + fprintf(stderr, "SQLITE SQL is \"%s\"\n", sql); +} +#endif + +/* Execute SQL on the sqlite database DB, and raise an SVN error if the + result is not okay. */ + +static svn_error_t * +util_sqlite_exec(sqlite3 *db, const char *sql, + sqlite3_callback callback, + void *callbackdata) +{ + char *err_msg; + svn_error_t *err; + if (sqlite3_exec(db, sql, NULL, NULL, &err_msg) != SQLITE_OK) + { + err = svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL, err_msg); + sqlite3_free(err_msg); + return err; + } + return SVN_NO_ERROR; +} + +/* The version number of the schema used to store the revprop index. */ +#define REVPROP_INDEX_SCHEMA_FORMAT 1 + +/* Return SVN_ERR_FS_GENERAL if the schema doesn't exist, + SVN_ERR_FS_UNSUPPORTED_FORMAT if the schema format is invalid, or + SVN_ERR_FS_SQLITE_ERROR if an sqlite error occurs during + validation. Return SVN_NO_ERROR if everything is okay. */ +static svn_error_t * +check_format(sqlite3 *db) +{ + svn_error_t *err = SVN_NO_ERROR; + sqlite3_stmt *stmt; + + SQLITE_ERR(sqlite3_prepare(db, "PRAGMA user_version;", -1, &stmt, NULL), db); + if (sqlite3_step(stmt) == SQLITE_ROW) + { + /* Validate that the schema exists as expected and that the + schema and repository versions match. */ + int schema_format = sqlite3_column_int(stmt, 0); + if (schema_format == REVPROP_INDEX_SCHEMA_FORMAT) + { + err = SVN_NO_ERROR; + } + else if (schema_format == 0) + { + /* This is likely a freshly-created database in which the + schema doesn't yet exist. */ + err = svn_error_create(SVN_ERR_FS_GENERAL, NULL, + _("Revprop schema format not set")); + } + else if (schema_format > REVPROP_INDEX_SCHEMA_FORMAT) + { + err = svn_error_createf(SVN_ERR_FS_UNSUPPORTED_FORMAT, NULL, + _("Revprop schema format %d " + "not recognized"), schema_format); + } + /* else, we may one day want to perform a schema migration. */ + + SQLITE_ERR(sqlite3_finalize(stmt), db); + } + else + { + err = svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL, + sqlite3_errmsg(db)); + } + return err; +} + +const char SVN_REVPROP_CREATE_SQL[] = "PRAGMA auto_vacuum = 1;" + APR_EOL_STR + "CREATE TABLE revprops (revision INTEGER NOT NULL, name TEXT NOT " + "NULL, value TEXT NOT NULL);" + APR_EOL_STR + "CREATE INDEX rp_revision_idx ON revprops (revision);" + APR_EOL_STR + "CREATE INDEX rp_name_idx ON revprops (name);" + APR_EOL_STR + "PRAGMA user_version = " APR_STRINGIFY(REVPROP_INDEX_SCHEMA_FORMAT) ";" + APR_EOL_STR; + +/* Open a connection in *DB to the revprop database under + REPOS_PATH. Validate the schema, creating it if it doesn't yet + exist. This provides a migration path for pre-1.5 repositories. */ +static svn_error_t * +open_db(sqlite3 **db, const char *repos_path, apr_pool_t *pool) +{ + svn_error_t *err; + const char *db_path = svn_path_join(repos_path, + SVN_FS_REVPROP__DB_NAME, pool); + SQLITE_ERR(sqlite3_open(db_path, db), *db); +#ifdef SQLITE3_DEBUG + sqlite3_trace(*db, sqlite_tracer, *db); +#endif + + /* Validate the schema. */ + err = check_format(*db); + if (err && err->apr_err == SVN_ERR_FS_GENERAL) + { + /* Assume that we've just created an empty index by way of + sqlite3_open() (likely from accessing a pre-1.5 repository), + and need to create the schema. */ + svn_error_clear(err); + err = util_sqlite_exec(*db, SVN_REVPROP_CREATE_SQL, NULL, NULL); + } + return err; +} + +/* Create an sqlite DB for our revprop index under PATH. Use POOL + for temporary allocations. */ +svn_error_t * +svn_fs_revprop__create_index(const char *path, apr_pool_t *pool) +{ + sqlite3 *db; + SVN_ERR(open_db(&db, path, pool)); + SQLITE_ERR(sqlite3_close(db), db); + return SVN_NO_ERROR; +} + + +/* Index the revprops for each path in REVPROPS (a mapping of const + char * -> to svn_string_t *). */ +static svn_error_t * +index_revprops(sqlite3 *db, + svn_revnum_t rev, + apr_hash_t *revprops, + apr_pool_t *pool) +{ + apr_hash_index_t *hi; + + for (hi = apr_hash_first(pool, revprops); + hi != NULL; + hi = apr_hash_next(hi)) + { + const void *name_v; + void *value_v; + const char *name; + svn_string_t *value; + sqlite3_stmt *stmt; + + apr_hash_this(hi, &name_v, NULL, &value_v); + name = name_v; + value = value_v; + + SQLITE_ERR(sqlite3_prepare(db, + "INSERT INTO revprops (revision, name, value) " + "VALUES (?, ?, ?);", -1, &stmt, NULL), + db); + SQLITE_ERR(sqlite3_bind_int64(stmt, 1, rev), db); + SQLITE_ERR(sqlite3_bind_text(stmt, 2, name, -1, SQLITE_TRANSIENT), + db); + SQLITE_ERR(sqlite3_bind_text(stmt, 3, value->data, value->len, + SQLITE_TRANSIENT), db); + + if (sqlite3_step(stmt) != SQLITE_DONE) + return svn_error_create(SVN_ERR_FS_SQLITE_ERROR, NULL, + sqlite3_errmsg(db)); + + SQLITE_ERR(sqlite3_finalize(stmt), db); + } + return SVN_NO_ERROR; +} + +/* Replace the revprops for revision REV in filesystem FS with the + contents of REVPROPS. */ +svn_error_t * +svn_fs_revprop__update_index(svn_fs_t *fs, + svn_revnum_t rev, + apr_hash_t *revprops, + apr_pool_t *pool) +{ + const char *deletestring; + sqlite3 *db; + + SVN_ERR(open_db(&db, fs->path, pool)); + SVN_ERR(util_sqlite_exec(db, "BEGIN TRANSACTION;", NULL, NULL)); + + /* Cleanup the leftovers of any previous, failed transactions + * involving NEW_REV. */ + deletestring = apr_psprintf(pool, + "DELETE FROM revprops WHERE " + "revision = %ld;", + rev); + SVN_ERR(util_sqlite_exec(db, deletestring, NULL, NULL)); + + /* Record the revprops from the current transaction. */ + SVN_ERR(index_revprops(db, rev, revprops, pool)); + + /* This is moved here from FSFS's commit_txn, because we don't want to + * write the final current file if the sqlite commit fails. + * On the other hand, if we commit the transaction and end up failing + * the current file, we just end up with inaccessible data in the + * database, not a real problem. */ + SVN_ERR(util_sqlite_exec(db, "COMMIT TRANSACTION;", NULL, NULL)); + SQLITE_ERR(sqlite3_close(db), db); + + return SVN_NO_ERROR; +} Property changes on: subversion/libsvn_fs_util/revprop-sqlite.c ___________________________________________________________________ Name: svn:eol-style +native === subversion/include/private/svn_fs_revprop.h ================================================================== --- subversion/include/private/svn_fs_revprop.h (revision 25329) +++ subversion/include/private/svn_fs_revprop.h (patch - level 1) @@ -0,0 +1,49 @@ +/* + * svn_fs_revprop.h: Declarations for the APIs of libsvn_fs_util to + * be consumed by only fs_* libs. + * + * ==================================================================== + * Copyright (c) 2007 CollabNet. All rights reserved. + * + * This software is licensed as described in the file COPYING, which + * you should have received as part of this distribution. The terms + * are also available at http://subversion.tigris.org/license-1.html. + * If newer versions of this license are posted there, you may use a + * newer version instead, at your option. + * + * This software consists of voluntary contributions made by many + * individuals. For exact contribution history, see the revision + * history and logs, available at http://subversion.tigris.org/. + * ==================================================================== + */ + +#ifndef SVN_FS_REVPROP_H +#define SVN_FS_REVPROP_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/* The name of the sqlite revprop database. */ +#define SVN_FS_REVPROP__DB_NAME "revprop.db" + +/* Create the revprop index under PATH. Use POOL for any temporary + allocations. */ +svn_error_t * +svn_fs_revprop__create_index(const char *path, apr_pool_t *pool); + +/* Update the revprop index for revision REV in filesystem FS. + REVPROPS are the revision properties for the revision (a mapping of + const char * -> svn_string_t *), possibly an empty hash. Use POOL + for any temporary allocations. */ +svn_error_t * +svn_fs_revprop__update_index(svn_fs_t *fs, + svn_revnum_t rev, + apr_hash_t *revprops, + apr_pool_t *pool); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* SVN_FS_REVPROP_H */ Property changes on: subversion/include/private/svn_fs_revprop.h ___________________________________________________________________ Name: svn:eol-style +native === subversion/libsvn_fs_fs/fs_fs.c ================================================================== --- subversion/libsvn_fs_fs/fs_fs.c (revision 25329) +++ subversion/libsvn_fs_fs/fs_fs.c (patch - level 1) @@ -49,6 +49,7 @@ #include "id.h" #include "private/svn_fs_mergeinfo.h" +#include "private/svn_fs_revprop.h" #include "private/svn_fs_util.h" #include "../libsvn_fs/fs-loader.h" @@ -1001,6 +1002,10 @@ SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, SVN_FS_MERGEINFO__DB_NAME, pool)); + /* Copy the revprop info. */ + SVN_ERR(svn_io_dir_file_copy(src_path, dst_path, SVN_FS_REVPROP__DB_NAME, + pool)); + /* Find the youngest revision from this current file. */ SVN_ERR(get_youngest(&youngest, dst_path, pool)); @@ -1811,6 +1816,8 @@ svn_fs_fs__path_rev(fs, rev, pool), pool)); + SVN_ERR(svn_fs_revprop__update_index(fs, rev, proplist, pool)); + return SVN_NO_ERROR; } @@ -4788,6 +4795,7 @@ SVN_ERR(svn_fs_fs__change_txn_prop(cb->txn, SVN_PROP_REVISION_DATE, &date, pool)); + SVN_ERR(svn_fs_fs__txn_proplist(&txnprops, cb->txn, pool)); /* Move the revprops file into place. */ revprop_filename = path_txn_props(cb->fs, cb->txn->id, pool); @@ -4799,6 +4807,10 @@ SVN_ERR(svn_fs_mergeinfo__update_index(cb->txn, new_rev, target_mergeinfo, pool)); + /* Update the revprop index. */ + SVN_ERR(svn_fs_revprop__update_index(cb->fs, new_rev, txnprops, + pool)); + /* Update the 'current' file. */ SVN_ERR(write_final_current(cb->fs, cb->txn->id, new_rev, start_node_id, start_copy_id, pool)); @@ -4934,6 +4946,7 @@ /* ### this should be before the format file */ SVN_ERR(svn_fs_mergeinfo__create_index(path, pool)); + SVN_ERR(svn_fs_revprop__create_index(path, pool)); return SVN_NO_ERROR; }