diff options
Diffstat (limited to 'subversion/libsvn_subr/sqlite.c')
-rw-r--r-- | subversion/libsvn_subr/sqlite.c | 584 |
1 files changed, 320 insertions, 264 deletions
diff --git a/subversion/libsvn_subr/sqlite.c b/subversion/libsvn_subr/sqlite.c index 7f5b279..295a11c 100644 --- a/subversion/libsvn_subr/sqlite.c +++ b/subversion/libsvn_subr/sqlite.c @@ -43,17 +43,37 @@ #endif #ifdef SVN_SQLITE_INLINE -/* Include sqlite3 inline, making all symbols private. */ - #define SQLITE_API static - #include <sqlite3.c> +/* Import the sqlite3 API vtable from sqlite3wrapper.c */ +# define SQLITE_OMIT_DEPRECATED +# include <sqlite3ext.h> +extern const sqlite3_api_routines *const svn_sqlite3__api_funcs; +extern int (*const svn_sqlite3__api_initialize)(void); +extern int (*const svn_sqlite3__api_config)(int, ...); +# define sqlite3_api svn_sqlite3__api_funcs +# define sqlite3_initialize svn_sqlite3__api_initialize +# define sqlite3_config svn_sqlite3__api_config #else - #include <sqlite3.h> +# include <sqlite3.h> #endif -#if !SQLITE_VERSION_AT_LEAST(3,6,18) -#error SQLite is too old -- version 3.6.18 is the minimum required version +#if !SQLITE_VERSION_AT_LEAST(3,7,12) +#error SQLite is too old -- version 3.7.12 is the minimum required version #endif +const char * +svn_sqlite__compiled_version(void) +{ + static const char sqlite_version[] = SQLITE_VERSION; + return sqlite_version; +} + +const char * +svn_sqlite__runtime_version(void) +{ + return sqlite3_libversion(); +} + + INTERNAL_STATEMENTS_SQL_DECLARE_STATEMENTS(internal_statements); @@ -84,7 +104,6 @@ struct svn_sqlite__db_t int nbr_statements; svn_sqlite__stmt_t **prepared_stmts; apr_pool_t *state_pool; - unsigned savepoint_nr; }; struct svn_sqlite__stmt_t @@ -121,8 +140,9 @@ struct svn_sqlite__value_t int sqlite_err__temp = (x); \ if (sqlite_err__temp != SQLITE_OK) \ return svn_error_createf(SQLITE_ERROR_CODE(sqlite_err__temp), \ - NULL, "sqlite: %s", \ - sqlite3_errmsg((db)->db3)); \ + NULL, "sqlite[S%d]: %s", \ + sqlite_err__temp, \ + sqlite3_errmsg((db)->db3)); \ } while (0) #define SQLITE_ERR_MSG(x, msg) do \ @@ -130,10 +150,15 @@ struct svn_sqlite__value_t int sqlite_err__temp = (x); \ if (sqlite_err__temp != SQLITE_OK) \ return svn_error_createf(SQLITE_ERROR_CODE(sqlite_err__temp), \ - NULL, "sqlite: %s", (msg)); \ + NULL, "sqlite[S%d]: %s", \ + sqlite_err__temp, msg); \ } while (0) +/* Time (in milliseconds) to wait for sqlite locks before giving up. */ +#define BUSY_TIMEOUT 10000 + + /* Convenience wrapper around exec_sql2(). */ #define exec_sql(db, sql) exec_sql2((db), (sql), SQLITE_OK) @@ -148,8 +173,9 @@ exec_sql2(svn_sqlite__db_t *db, const char *sql, int ignored_err) if (sqlite_err != SQLITE_OK && sqlite_err != ignored_err) { svn_error_t *err = svn_error_createf(SQLITE_ERROR_CODE(sqlite_err), NULL, - _("%s, executing statement '%s'"), - err_msg, sql); + _("sqlite[S%d]: %s," + " executing statement '%s'"), + sqlite_err, err_msg, sql); sqlite3_free(err_msg); return err; } @@ -200,6 +226,29 @@ svn_sqlite__get_statement(svn_sqlite__stmt_t **stmt, svn_sqlite__db_t *db, return SVN_NO_ERROR; } +/* Like svn_sqlite__get_statement but gets an internal statement. + + All internal statements that use this api are executed with step_done(), + so we don't need the fallback reset handling here or in the pool cleanup */ +static svn_error_t * +get_internal_statement(svn_sqlite__stmt_t **stmt, svn_sqlite__db_t *db, + int stmt_idx) +{ + /* The internal statements are stored after the registered statements */ + int prep_idx = db->nbr_statements + stmt_idx; + SVN_ERR_ASSERT(stmt_idx < STMT_INTERNAL_LAST); + + if (db->prepared_stmts[prep_idx] == NULL) + SVN_ERR(prepare_statement(&db->prepared_stmts[prep_idx], db, + internal_statements[stmt_idx], + db->state_pool)); + + *stmt = db->prepared_stmts[prep_idx]; + + return SVN_NO_ERROR; +} + + static svn_error_t * step_with_expectation(svn_sqlite__stmt_t* stmt, svn_boolean_t expecting_row) @@ -213,8 +262,8 @@ step_with_expectation(svn_sqlite__stmt_t* stmt, return svn_error_create(SVN_ERR_SQLITE_ERROR, svn_sqlite__reset(stmt), expecting_row - ? _("Expected database row missing") - : _("Extra database row found")); + ? _("sqlite: Expected database row missing") + : _("sqlite: Extra database row found")); return SVN_NO_ERROR; } @@ -243,7 +292,8 @@ svn_sqlite__step(svn_boolean_t *got_row, svn_sqlite__stmt_t *stmt) svn_error_t *err1, *err2; err1 = svn_error_createf(SQLITE_ERROR_CODE(sqlite_result), NULL, - "sqlite: %s", sqlite3_errmsg(stmt->db->db3)); + "sqlite[S%d]: %s", + sqlite_result, sqlite3_errmsg(stmt->db->db3)); err2 = svn_sqlite__reset(stmt); return svn_error_compose_create(err1, err2); } @@ -296,7 +346,13 @@ vbindf(svn_sqlite__stmt_t *stmt, const char *fmt, va_list ap) va_arg(ap, const char *))); break; + case 'd': + SVN_ERR(svn_sqlite__bind_int(stmt, count, + va_arg(ap, int))); + break; + case 'i': + case 'L': SVN_ERR(svn_sqlite__bind_int64(stmt, count, va_arg(ap, apr_int64_t))); break; @@ -420,8 +476,28 @@ svn_sqlite__bind_properties(svn_sqlite__stmt_t *stmt, if (props == NULL) return svn_error_trace(svn_sqlite__bind_blob(stmt, slot, NULL, 0)); - SVN_ERR(svn_skel__unparse_proplist(&skel, (apr_hash_t *)props, - scratch_pool)); + SVN_ERR(svn_skel__unparse_proplist(&skel, props, scratch_pool)); + properties = svn_skel__unparse(skel, scratch_pool); + return svn_error_trace(svn_sqlite__bind_blob(stmt, + slot, + properties->data, + properties->len)); +} + +svn_error_t * +svn_sqlite__bind_iprops(svn_sqlite__stmt_t *stmt, + int slot, + const apr_array_header_t *inherited_props, + apr_pool_t *scratch_pool) +{ + svn_skel_t *skel; + svn_stringbuf_t *properties; + + if (inherited_props == NULL) + return svn_error_trace(svn_sqlite__bind_blob(stmt, slot, NULL, 0)); + + SVN_ERR(svn_skel__unparse_iproplist(&skel, inherited_props, + scratch_pool, scratch_pool)); properties = svn_skel__unparse(skel, scratch_pool); return svn_error_trace(svn_sqlite__bind_blob(stmt, slot, @@ -509,6 +585,21 @@ svn_sqlite__column_token(svn_sqlite__stmt_t *stmt, return svn_token__from_word_strict(map, word); } +int +svn_sqlite__column_token_null(svn_sqlite__stmt_t *stmt, + int column, + const svn_token_map_t *map, + int null_val) +{ + /* cast from 'unsigned char' to regular 'char' */ + const char *word = (const char *)sqlite3_column_text(stmt->s3stmt, column); + + if (!word) + return null_val; + + return svn_token__from_word_strict(map, word); +} + svn_error_t * svn_sqlite__column_properties(apr_hash_t **props, svn_sqlite__stmt_t *stmt, @@ -535,6 +626,31 @@ svn_sqlite__column_properties(apr_hash_t **props, } svn_error_t * +svn_sqlite__column_iprops(apr_array_header_t **iprops, + svn_sqlite__stmt_t *stmt, + int column, + apr_pool_t *result_pool, + apr_pool_t *scratch_pool) +{ + apr_size_t len; + const void *val; + + /* svn_skel__parse_iprops copies everything needed to result_pool */ + val = svn_sqlite__column_blob(stmt, column, &len, NULL); + if (val == NULL) + { + *iprops = NULL; + return SVN_NO_ERROR; + } + + SVN_ERR(svn_skel__parse_iprops(iprops, + svn_skel__parse(val, len, scratch_pool), + result_pool)); + + return SVN_NO_ERROR; +} + +svn_error_t * svn_sqlite__column_checksum(const svn_checksum_t **checksum, svn_sqlite__stmt_t *stmt, int column, apr_pool_t *result_pool) @@ -580,108 +696,6 @@ svn_sqlite__reset(svn_sqlite__stmt_t *stmt) svn_error_t * -svn_sqlite__set_schema_version(svn_sqlite__db_t *db, - int version, - apr_pool_t *scratch_pool) -{ - const char *pragma_cmd = apr_psprintf(scratch_pool, - "PRAGMA user_version = %d;", - version); - - return svn_error_trace(exec_sql(db, pragma_cmd)); -} - - -/* Time (in milliseconds) to wait for sqlite locks before giving up. */ -#define BUSY_TIMEOUT 10000 - - -#if 0 -/* - * EXAMPLE - * - * The following provide an example for a series of SQL statements to - * create/upgrade a SQLite schema across multiple "formats". This array - * and integer would be passed into svn_sqlite__open(). - */ -static const char *schema_create_sql[] = { - NULL, /* An empty database is format 0 */ - - /* USER_VERSION 1 */ - "PRAGMA auto_vacuum = 1;" - APR_EOL_STR - "CREATE TABLE mergeinfo (revision INTEGER NOT NULL, mergedfrom TEXT NOT " - "NULL, mergedto TEXT NOT NULL, mergedrevstart INTEGER NOT NULL, " - "mergedrevend INTEGER NOT NULL, inheritable INTEGER NOT NULL);" - APR_EOL_STR - "CREATE INDEX mi_mergedfrom_idx ON mergeinfo (mergedfrom);" - APR_EOL_STR - "CREATE INDEX mi_mergedto_idx ON mergeinfo (mergedto);" - APR_EOL_STR - "CREATE INDEX mi_revision_idx ON mergeinfo (revision);" - APR_EOL_STR - "CREATE TABLE mergeinfo_changed (revision INTEGER NOT NULL, path TEXT " - "NOT NULL);" - APR_EOL_STR - "CREATE UNIQUE INDEX mi_c_revpath_idx ON mergeinfo_changed (revision, path);" - APR_EOL_STR - "CREATE INDEX mi_c_path_idx ON mergeinfo_changed (path);" - APR_EOL_STR - "CREATE INDEX mi_c_revision_idx ON mergeinfo_changed (revision);" - APR_EOL_STR, - - /* USER_VERSION 2 */ - "CREATE TABLE node_origins (node_id TEXT NOT NULL, node_rev_id TEXT NOT " - "NULL);" - APR_EOL_STR - "CREATE UNIQUE INDEX no_ni_idx ON node_origins (node_id);" - APR_EOL_STR -}; - -static const int latest_schema_format = - sizeof(schema_create_sql)/sizeof(schema_create_sql[0]) - 1; - -#endif - -struct upgrade_baton -{ - int current_schema; - int latest_schema; - const char * const *upgrade_sql; -}; - - -/* This implements svn_sqlite__transaction_callback_t */ -static svn_error_t * -upgrade_format(void *baton, - svn_sqlite__db_t *db, - apr_pool_t *scratch_pool) -{ - struct upgrade_baton *ub = baton; - int current_schema = ub->current_schema; - apr_pool_t *iterpool = svn_pool_create(scratch_pool); - - while (current_schema < ub->latest_schema) - { - svn_pool_clear(iterpool); - - /* Go to the next schema */ - current_schema++; - - /* Run the upgrade SQL */ - if (ub->upgrade_sql[current_schema]) - SVN_ERR(exec_sql(db, ub->upgrade_sql[current_schema])); - - /* Update the user version pragma */ - SVN_ERR(svn_sqlite__set_schema_version(db, current_schema, iterpool)); - } - - svn_pool_destroy(iterpool); - - return SVN_NO_ERROR; -} - -svn_error_t * svn_sqlite__read_schema_version(int *version, svn_sqlite__db_t *db, apr_pool_t *scratch_pool) @@ -697,41 +711,6 @@ svn_sqlite__read_schema_version(int *version, } -/* Check the schema format of the database, upgrading it if necessary. - Return SVN_ERR_SQLITE_UNSUPPORTED_SCHEMA if the schema format is too new, - or SVN_ERR_SQLITE_ERROR if an sqlite error occurs during validation. - Return SVN_NO_ERROR if everything is okay. */ -static svn_error_t * -check_format(svn_sqlite__db_t *db, - int latest_schema, - const char * const *upgrade_sql, - apr_pool_t *scratch_pool) -{ - int current_schema; - - /* Validate that the schema exists as expected. */ - SVN_ERR(svn_sqlite__read_schema_version(¤t_schema, db, scratch_pool)); - - if (current_schema == latest_schema) - return SVN_NO_ERROR; - - if (current_schema < latest_schema) - { - struct upgrade_baton ub; - - ub.current_schema = current_schema; - ub.latest_schema = latest_schema; - ub.upgrade_sql = upgrade_sql; - - return svn_error_trace(svn_sqlite__with_transaction( - db, upgrade_format, &ub, scratch_pool)); - } - - return svn_error_createf(SVN_ERR_SQLITE_UNSUPPORTED_SCHEMA, NULL, - _("Schema format %d not recognized"), - current_schema); -} - static volatile svn_atomic_t sqlite_init_state = 0; /* If possible, verify that SQLite was compiled in a thread-safe @@ -740,12 +719,12 @@ static volatile svn_atomic_t sqlite_init_state = 0; static svn_error_t * init_sqlite(void *baton, apr_pool_t *pool) { - if (sqlite3_libversion_number() < SQLITE_VERSION_NUMBER) + if (sqlite3_libversion_number() < SVN_SQLITE_MIN_VERSION_NUMBER) { return svn_error_createf( SVN_ERR_SQLITE_ERROR, NULL, _("SQLite compiled for %s, but running with %s"), - SQLITE_VERSION, sqlite3_libversion()); + SVN_SQLITE_MIN_VERSION, sqlite3_libversion()); } #if APR_HAS_THREADS @@ -764,8 +743,8 @@ init_sqlite(void *baton, apr_pool_t *pool) { int err = sqlite3_config(SQLITE_CONFIG_MULTITHREAD); if (err != SQLITE_OK && err != SQLITE_MISUSE) - return svn_error_create(SQLITE_ERROR_CODE(err), NULL, - _("Could not configure SQLite")); + return svn_error_createf(SQLITE_ERROR_CODE(err), NULL, + _("Could not configure SQLite [S%d]"), err); } SQLITE_ERR_MSG(sqlite3_initialize(), _("Could not initialize SQLite")); @@ -790,22 +769,35 @@ internal_open(sqlite3 **db3, const char *path, svn_sqlite__mode_t mode, else SVN_ERR_MALFUNCTION(); - /* If this flag is defined (3.6.x), then let's turn off SQLite's mutexes. - All svn objects are single-threaded, so we can already guarantee that - our use of the SQLite handle will be serialized properly. + /* Turn off SQLite's mutexes. All svn objects are single-threaded, + so we can already guarantee that our use of the SQLite handle + will be serialized properly. Note: in 3.6.x, we've already config'd SQLite into MULTITHREAD mode, so this is probably redundant, but if we are running in a process where somebody initialized SQLite before us it is needed anyway. */ -#ifdef SQLITE_OPEN_NOMUTEX flags |= SQLITE_OPEN_NOMUTEX; + +#if !defined(WIN32) && !defined(SVN_SQLITE_INLINE) + if (mode == svn_sqlite__mode_rwcreate) + { + svn_node_kind_t kind; + + /* Create the file before SQLite to avoid any permissions + problems with an SQLite build that uses the default + SQLITE_DEFAULT_FILE_PERMISSIONS of 644 modified by umask. + We simply want umask permissions. */ + SVN_ERR(svn_io_check_path(path, &kind, scratch_pool)); + if (kind == svn_node_none) + SVN_ERR(svn_io_file_create(path, "", scratch_pool)); + } #endif /* Open the database. Note that a handle is returned, even when an error occurs (except for out-of-memory); thus, we can safely use it to extract an error message and construct an svn_error_t. */ { - /* We'd like to use SQLITE_ERR_MSG here, but we can't since it would + /* We'd like to use SQLITE_ERR here, but we can't since it would just return an error and leave the database open. So, we need to do this manually. */ /* ### SQLITE_CANTOPEN */ @@ -819,8 +811,7 @@ internal_open(sqlite3 **db3, const char *path, svn_sqlite__mode_t mode, error than the close error at this point. */ sqlite3_close(*db3); - return svn_error_createf(SQLITE_ERROR_CODE(err_code), NULL, - "sqlite: %s: '%s'", msg, path); + SQLITE_ERR_MSG(err_code, msg); } } } @@ -832,22 +823,6 @@ internal_open(sqlite3 **db3, const char *path, svn_sqlite__mode_t mode, return SVN_NO_ERROR; } -svn_error_t * -svn_sqlite__get_schema_version(int *version, - const char *path, - apr_pool_t *scratch_pool) -{ - svn_sqlite__db_t db; - - SVN_ERR(svn_atomic__init_once(&sqlite_init_state, - init_sqlite, NULL, scratch_pool)); - SVN_ERR(internal_open(&db.db3, path, svn_sqlite__mode_readonly, - scratch_pool)); - SVN_ERR(svn_sqlite__read_schema_version(version, &db, scratch_pool)); - SQLITE_ERR(sqlite3_close(db.db3), &db); - - return SVN_NO_ERROR; -} /* APR cleanup function used to close the database when its pool is destroyed. DATA should be the svn_sqlite__db_t handle for the database. */ @@ -867,8 +842,32 @@ close_apr(void *data) for (i = 0; i < db->nbr_statements; i++) { if (db->prepared_stmts[i]) - err = svn_error_compose_create( + { + if (db->prepared_stmts[i]->needs_reset) + { +#ifdef SVN_DEBUG + const char *stmt_text = db->statement_strings[i]; + stmt_text = stmt_text; /* Provide value for debugger */ + + SVN_ERR_MALFUNCTION_NO_RETURN(); +#else + err = svn_error_compose_create( + err, + svn_sqlite__reset(db->prepared_stmts[i])); +#endif + } + err = svn_error_compose_create( svn_sqlite__finalize(db->prepared_stmts[i]), err); + } + } + /* And finalize any used internal statements */ + for (; i < db->nbr_statements + STMT_INTERNAL_LAST; i++) + { + if (db->prepared_stmts[i]) + { + err = svn_error_compose_create( + svn_sqlite__finalize(db->prepared_stmts[i]), err); + } } result = sqlite3_close(db->db3); @@ -882,7 +881,7 @@ close_apr(void *data) } if (result != SQLITE_OK) - return SQLITE_ERROR_CODE(result); + return SQLITE_ERROR_CODE(result); /* ### lossy */ db->db3 = NULL; @@ -893,7 +892,7 @@ close_apr(void *data) svn_error_t * svn_sqlite__open(svn_sqlite__db_t **db, const char *path, svn_sqlite__mode_t mode, const char * const statements[], - int latest_schema, const char * const *upgrade_sql, + int unused1, const char * const *unused2, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { SVN_ERR(svn_atomic__init_once(&sqlite_init_state, @@ -903,6 +902,18 @@ svn_sqlite__open(svn_sqlite__db_t **db, const char *path, SVN_ERR(internal_open(&(*db)->db3, path, mode, scratch_pool)); +#if SQLITE_VERSION_NUMBER >= 3008000 && SQLITE_VERSION_NUMBER < 3009000 + /* disable SQLITE_ENABLE_STAT3/4 from 3.8.1 - 3.8.3 (but not 3.8.3.1+) + * to prevent using it when it's buggy. + * See: https://www.sqlite.org/src/info/4c86b126f2 */ + if (sqlite3_libversion_number() > 3008000 && + sqlite3_libversion_number() < 3008004 && + strcmp(sqlite3_sourceid(),"2014-02-11")<0) + { + sqlite3_test_control(SQLITE_TESTCTRL_OPTIMIZATIONS, (*db)->db3, 0x800); + } +#endif + #ifdef SQLITE3_DEBUG sqlite3_trace((*db)->db3, sqlite_tracer, (*db)->db3); #endif @@ -910,23 +921,9 @@ svn_sqlite__open(svn_sqlite__db_t **db, const char *path, sqlite3_profile((*db)->db3, sqlite_profiler, (*db)->db3); #endif - /* Work around a bug in SQLite 3.7.7. The bug was fixed in SQLite 3.7.7.1. - - See: - - Date: Sun, 26 Jun 2011 18:52:14 -0400 - From: Richard Hipp <drh@sqlite.org> - To: General Discussion of SQLite Database <sqlite-users@sqlite.org> - Cc: dev@subversion.apache.org - Subject: Re: [sqlite] PRAGMA bug in 3.7.7 (but fine in 3.7.6.3) - Message-ID: <BANLkTimDypWGY-8tHFgJsTxN6ty6OkdJ0Q@mail.gmail.com> - */ + /* ### simplify this. remnants of some old SQLite compat code. */ { int ignored_err = SQLITE_OK; -#if !SQLITE_VERSION_AT_LEAST(3,7,8) && defined(SQLITE_SCHEMA) - if (!strcmp(sqlite3_libversion(), "3.7.7")) - ignored_err = SQLITE_SCHEMA; -#endif SVN_ERR(exec_sql2(*db, "PRAGMA case_sensitive_like=1;", ignored_err)); } @@ -948,24 +945,34 @@ svn_sqlite__open(svn_sqlite__db_t **db, const char *path, /* Enable recursive triggers so that a user trigger will fire in the deletion phase of an INSERT OR REPLACE statement. Requires SQLite >= 3.6.18 */ - "PRAGMA recursive_triggers=ON;")); - -#if SQLITE_VERSION_AT_LEAST(3,6,19) && defined(SVN_DEBUG) + "PRAGMA recursive_triggers=ON;" + /* Enforce current Sqlite default behavior. Some distributions + might change the Sqlite defaults without realizing how this + affects application(read: Subversion) performance/behavior. */ + "PRAGMA foreign_keys=OFF;" /* SQLITE_DEFAULT_FOREIGN_KEYS*/ + "PRAGMA locking_mode = NORMAL;" /* SQLITE_DEFAULT_LOCKING_MODE */ + )); + +#if defined(SVN_DEBUG) /* When running in debug mode, enable the checking of foreign key constraints. This has possible performance implications, so we don't bother to do it for production...for now. */ SVN_ERR(exec_sql(*db, "PRAGMA foreign_keys=ON;")); #endif +#ifdef SVN_SQLITE_REVERSE_UNORDERED_SELECTS + /* When enabled, this PRAGMA causes SELECT statements without an ORDER BY + clause to emit their results in the reverse order of what they normally + would. This can help detecting invalid assumptions about the result + order.*/ + SVN_ERR(exec_sql(*db, "PRAGMA reverse_unordered_selects=ON;")); +#endif + /* Store temporary tables in RAM instead of in temporary files, but don't fail on this if this option is disabled in the sqlite compilation by setting SQLITE_TEMP_STORE to 0 (always to disk) */ svn_error_clear(exec_sql(*db, "PRAGMA temp_store = MEMORY;")); - /* Validate the schema, upgrading if necessary. */ - if (upgrade_sql != NULL) - SVN_ERR(check_format(*db, latest_schema, upgrade_sql, scratch_pool)); - /* Store the provided statements. */ if (statements) { @@ -976,11 +983,19 @@ svn_sqlite__open(svn_sqlite__db_t **db, const char *path, statements++; (*db)->nbr_statements++; } - (*db)->prepared_stmts = apr_pcalloc(result_pool, (*db)->nbr_statements + + (*db)->prepared_stmts = apr_pcalloc( + result_pool, + ((*db)->nbr_statements + STMT_INTERNAL_LAST) * sizeof(svn_sqlite__stmt_t *)); } else - (*db)->nbr_statements = 0; + { + (*db)->nbr_statements = 0; + (*db)->prepared_stmts = apr_pcalloc(result_pool, + (0 + STMT_INTERNAL_LAST) + * sizeof(svn_sqlite__stmt_t *)); + } (*db)->state_pool = result_pool; apr_pool_cleanup_register(result_pool, *db, close_apr, apr_pool_cleanup_null); @@ -1021,22 +1036,54 @@ reset_all_statements(svn_sqlite__db_t *db, return err; } -/* The body of svn_sqlite__with_transaction() and - svn_sqlite__with_immediate_transaction(), which see. */ -static svn_error_t * -with_transaction(svn_sqlite__db_t *db, - svn_sqlite__transaction_callback_t cb_func, - void *cb_baton, - apr_pool_t *scratch_pool /* NULL allowed */) +svn_error_t * +svn_sqlite__begin_transaction(svn_sqlite__db_t *db) { - svn_error_t *err; + svn_sqlite__stmt_t *stmt; - err = cb_func(cb_baton, db, scratch_pool); + SVN_ERR(get_internal_statement(&stmt, db, + STMT_INTERNAL_BEGIN_TRANSACTION)); + SVN_ERR(svn_sqlite__step_done(stmt)); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_sqlite__begin_immediate_transaction(svn_sqlite__db_t *db) +{ + svn_sqlite__stmt_t *stmt; + + SVN_ERR(get_internal_statement(&stmt, db, + STMT_INTERNAL_BEGIN_IMMEDIATE_TRANSACTION)); + SVN_ERR(svn_sqlite__step_done(stmt)); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_sqlite__begin_savepoint(svn_sqlite__db_t *db) +{ + svn_sqlite__stmt_t *stmt; + + SVN_ERR(get_internal_statement(&stmt, db, + STMT_INTERNAL_SAVEPOINT_SVN)); + SVN_ERR(svn_sqlite__step_done(stmt)); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_sqlite__finish_transaction(svn_sqlite__db_t *db, + svn_error_t *err) +{ + svn_sqlite__stmt_t *stmt; /* Commit or rollback the sqlite transaction. */ if (err) { - svn_error_t *err2 = exec_sql(db, "ROLLBACK TRANSACTION;"); + svn_error_t *err2; + + err2 = get_internal_statement(&stmt, db, + STMT_INTERNAL_ROLLBACK_TRANSACTION); + if (!err2) + err2 = svn_sqlite__step_done(stmt); if (err2 && err2->apr_err == SVN_ERR_SQLITE_BUSY) { @@ -1061,7 +1108,7 @@ with_transaction(svn_sqlite__db_t *db, err2 = reset_all_statements(db, err2); err2 = svn_error_compose_create( - exec_sql(db, "ROLLBACK TRANSACTION;"), + svn_sqlite__step_done(stmt), err2); } @@ -1069,74 +1116,83 @@ with_transaction(svn_sqlite__db_t *db, err2); } - return svn_error_trace(exec_sql(db, "COMMIT TRANSACTION;")); + SVN_ERR(get_internal_statement(&stmt, db, STMT_INTERNAL_COMMIT_TRANSACTION)); + return svn_error_trace(svn_sqlite__step_done(stmt)); } svn_error_t * -svn_sqlite__with_transaction(svn_sqlite__db_t *db, - svn_sqlite__transaction_callback_t cb_func, - void *cb_baton, - apr_pool_t *scratch_pool /* NULL allowed */) +svn_sqlite__finish_savepoint(svn_sqlite__db_t *db, + svn_error_t *err) { - SVN_ERR(exec_sql(db, "BEGIN TRANSACTION;")); - return svn_error_trace(with_transaction(db, cb_func, cb_baton, - scratch_pool)); -} - -svn_error_t * -svn_sqlite__with_immediate_transaction( - svn_sqlite__db_t *db, - svn_sqlite__transaction_callback_t cb_func, - void *cb_baton, - apr_pool_t *scratch_pool /* NULL allowed */) -{ - SVN_ERR(exec_sql(db, "BEGIN IMMEDIATE TRANSACTION;")); - return svn_error_trace(with_transaction(db, cb_func, cb_baton, - scratch_pool)); -} - -svn_error_t * -svn_sqlite__with_lock(svn_sqlite__db_t *db, - svn_sqlite__transaction_callback_t cb_func, - void *cb_baton, - apr_pool_t *scratch_pool) -{ - svn_error_t *err; - int savepoint = db->savepoint_nr++; - /* This buffer is plenty big to hold the SAVEPOINT and RELEASE commands. */ - char buf[32]; - - snprintf(buf, sizeof(buf), "SAVEPOINT s%u", savepoint); - SVN_ERR(exec_sql(db, buf)); - err = cb_func(cb_baton, db, scratch_pool); + svn_sqlite__stmt_t *stmt; if (err) { svn_error_t *err2; - snprintf(buf, sizeof(buf), "ROLLBACK TO s%u", savepoint); - err2 = exec_sql(db, buf); + err2 = get_internal_statement(&stmt, db, + STMT_INTERNAL_ROLLBACK_TO_SAVEPOINT_SVN); + + if (!err2) + err2 = svn_sqlite__step_done(stmt); if (err2 && err2->apr_err == SVN_ERR_SQLITE_BUSY) { /* Ok, we have a major problem. Some statement is still open, which makes it impossible to release this savepoint. - ### See huge comment in svn_sqlite__with_transaction for + ### See huge comment in svn_sqlite__finish_transaction for further details */ err2 = reset_all_statements(db, err2); - err2 = svn_error_compose_create(exec_sql(db, buf), err2); + err2 = svn_error_compose_create(svn_sqlite__step_done(stmt), err2); } - snprintf(buf, sizeof(buf), "RELEASE s%u", savepoint); - err2 = svn_error_compose_create(exec_sql(db, buf), err2); + err = svn_error_compose_create(err, err2); + err2 = get_internal_statement(&stmt, db, + STMT_INTERNAL_RELEASE_SAVEPOINT_SVN); + + if (!err2) + err2 = svn_sqlite__step_done(stmt); return svn_error_trace(svn_error_compose_create(err, err2)); } - snprintf(buf, sizeof(buf), "RELEASE s%u", savepoint); - return svn_error_trace(exec_sql(db, buf)); + SVN_ERR(get_internal_statement(&stmt, db, + STMT_INTERNAL_RELEASE_SAVEPOINT_SVN)); + + return svn_error_trace(svn_sqlite__step_done(stmt)); +} + +svn_error_t * +svn_sqlite__with_transaction(svn_sqlite__db_t *db, + svn_sqlite__transaction_callback_t cb_func, + void *cb_baton, + apr_pool_t *scratch_pool /* NULL allowed */) +{ + SVN_SQLITE__WITH_TXN(cb_func(cb_baton, db, scratch_pool), db); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_sqlite__with_immediate_transaction( + svn_sqlite__db_t *db, + svn_sqlite__transaction_callback_t cb_func, + void *cb_baton, + apr_pool_t *scratch_pool /* NULL allowed */) +{ + SVN_SQLITE__WITH_IMMEDIATE_TXN(cb_func(cb_baton, db, scratch_pool), db); + return SVN_NO_ERROR; +} + +svn_error_t * +svn_sqlite__with_lock(svn_sqlite__db_t *db, + svn_sqlite__transaction_callback_t cb_func, + void *cb_baton, + apr_pool_t *scratch_pool /* NULL allowed */) +{ + SVN_SQLITE__WITH_LOCK(cb_func(cb_baton, db, scratch_pool), db); + return SVN_NO_ERROR; } svn_error_t * @@ -1147,7 +1203,7 @@ svn_sqlite__hotcopy(const char *src_path, svn_sqlite__db_t *src_db; SVN_ERR(svn_sqlite__open(&src_db, src_path, svn_sqlite__mode_readonly, - internal_statements, 0, NULL, + NULL, 0, NULL, scratch_pool, scratch_pool)); { |