diff options
author | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
---|---|---|
committer | Lorry Tar Creator <lorry-tar-importer@lorry> | 2017-08-05 16:22:51 +0000 |
commit | cf46733632c7279a9fd0fe6ce26f9185a4ae82a9 (patch) | |
tree | da27775a2161723ef342e91af41a8b51fedef405 /subversion/libsvn_subr/sqlite.c | |
parent | bb0ef45f7c46b0ae221b26265ef98a768c33f820 (diff) | |
download | subversion-tarball-master.tar.gz |
subversion-1.9.7HEADsubversion-1.9.7master
Diffstat (limited to 'subversion/libsvn_subr/sqlite.c')
-rw-r--r-- | subversion/libsvn_subr/sqlite.c | 515 |
1 files changed, 394 insertions, 121 deletions
diff --git a/subversion/libsvn_subr/sqlite.c b/subversion/libsvn_subr/sqlite.c index 295a11c..18124a3 100644 --- a/subversion/libsvn_subr/sqlite.c +++ b/subversion/libsvn_subr/sqlite.c @@ -37,6 +37,15 @@ #include "private/svn_atomic.h" #include "private/svn_skel.h" #include "private/svn_token.h" +#ifdef WIN32 +#include "private/svn_io_private.h" +#include "private/svn_utf_private.h" +#endif + +#ifdef SVN_UNICODE_NORMALIZATION_FIXES +#include "private/svn_utf_private.h" +#include "private/svn_string_private.h" +#endif /* SVN_UNICODE_NORMALIZATION_FIXES */ #ifdef SQLITE3_DEBUG #include "private/svn_debug.h" @@ -60,6 +69,17 @@ extern int (*const svn_sqlite3__api_config)(int, ...); #error SQLite is too old -- version 3.7.12 is the minimum required version #endif +#ifndef SQLITE_DETERMINISTIC +#define SQLITE_DETERMINISTIC 0 +#endif + +#ifdef SVN_UNICODE_NORMALIZATION_FIXES +/* Limit the length of a GLOB or LIKE pattern. */ +#ifndef SQLITE_MAX_LIKE_PATTERN_LENGTH +# define SQLITE_MAX_LIKE_PATTERN_LENGTH 50000 +#endif +#endif /* SVN_UNICODE_NORMALIZATION_FIXES */ + const char * svn_sqlite__compiled_version(void) { @@ -97,6 +117,23 @@ sqlite_profiler(void *data, const char *sql, sqlite3_uint64 duration) } #endif +#if defined(SVN_DEBUG) && defined(SQLITE_CONFIG_LOG) +static void +sqlite_error_log(void* baton, int err, const char* msg) +{ + fprintf(SVN_DBG_OUTPUT, "DBG: sqlite[S%d]: %s\n", err, msg); +} +#endif + +void +svn_sqlite__dbg_enable_errorlog(void) +{ +#if defined(SVN_DEBUG) && defined(SQLITE_CONFIG_LOG) + sqlite3_config(SQLITE_CONFIG_LOG, sqlite_error_log, (void*)NULL /* baton */); +#endif +} + + struct svn_sqlite__db_t { sqlite3 *db3; @@ -104,6 +141,13 @@ struct svn_sqlite__db_t int nbr_statements; svn_sqlite__stmt_t **prepared_stmts; apr_pool_t *state_pool; + +#ifdef SVN_UNICODE_NORMALIZATION_FIXES + /* Buffers for SQLite extensoins. */ + svn_membuf_t sqlext_buf1; + svn_membuf_t sqlext_buf2; + svn_membuf_t sqlext_buf3; +#endif /* SVN_UNICODE_NORMALIZATION_FIXES */ }; struct svn_sqlite__stmt_t @@ -145,6 +189,21 @@ struct svn_sqlite__value_t sqlite3_errmsg((db)->db3)); \ } while (0) +#define SQLITE_ERR_CLOSE(x, db, pool) do \ +{ \ + int sqlite_err__temp = (x); \ + if (sqlite_err__temp != SQLITE_OK) \ + { \ + const char *sqlite_err__msg \ + = apr_pstrdup(pool, sqlite3_errmsg((db)->db3)); \ + return svn_error_compose_create( \ + svn_error_createf(SQLITE_ERROR_CODE(sqlite_err__temp), \ + NULL, "sqlite[S%d]: %s", \ + sqlite_err__temp, sqlite_err__msg), \ + svn_sqlite__close(db)); \ + } \ +} while (0) + #define SQLITE_ERR_MSG(x, msg) do \ { \ int sqlite_err__temp = (x); \ @@ -154,6 +213,13 @@ struct svn_sqlite__value_t sqlite_err__temp, msg); \ } while (0) +#define SVN_ERR_CLOSE(x, db) do \ +{ \ + svn_error_t *svn__err = (x); \ + if (svn__err) \ + return svn_error_compose_create(svn__err, svn_sqlite__close(db)); \ +} while (0) + /* Time (in milliseconds) to wait for sqlite locks before giving up. */ #define BUSY_TIMEOUT 10000 @@ -688,9 +754,20 @@ svn_sqlite__finalize(svn_sqlite__stmt_t *stmt) svn_error_t * svn_sqlite__reset(svn_sqlite__stmt_t *stmt) { - SQLITE_ERR(sqlite3_reset(stmt->s3stmt), stmt->db); - SQLITE_ERR(sqlite3_clear_bindings(stmt->s3stmt), stmt->db); + /* No need to reset again after a first attempt */ stmt->needs_reset = FALSE; + + /* Clear bindings first, as there are no documented reasons + why this would ever fail, but keeping variable bindings + when reset is not what we expect. */ + SQLITE_ERR(sqlite3_clear_bindings(stmt->s3stmt), stmt->db); + + /* Reset last, as this *will* fail if the statement failed since + the last time it was reset, while reporting just the same failure. + (In this case the statement is also properly reset). + + See the sqlite3_reset() documentation for more details. */ + SQLITE_ERR(sqlite3_reset(stmt->s3stmt), stmt->db); return SVN_NO_ERROR; } @@ -754,8 +831,8 @@ init_sqlite(void *baton, apr_pool_t *pool) } static svn_error_t * -internal_open(sqlite3 **db3, const char *path, svn_sqlite__mode_t mode, - apr_pool_t *scratch_pool) +internal_open(svn_sqlite__db_t *db, const char *path, svn_sqlite__mode_t mode, + apr_int32_t timeout, apr_pool_t *scratch_pool) { { int flags; @@ -789,36 +866,59 @@ internal_open(sqlite3 **db3, const char *path, svn_sqlite__mode_t mode, 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)); + { + /* Another thread may have created the file, that's OK. */ + svn_error_t *err = svn_io_file_create_empty(path, scratch_pool); + if (err && !APR_STATUS_IS_EEXIST(err->apr_err)) + return svn_error_trace(err); + svn_error_clear(err); + } } #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. */ + extract an error message and construct an svn_error_t. SQLite always + requires sqlite3_close() after sqlite3_open_v2() while Subversion + typically does not require close() after an open() that returns an + error. So we must ensure we close the handle if this function, or + the caller svn_sqlite__open, returns an error to the application. */ { - /* 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 */ - int err_code = sqlite3_open_v2(path, db3, flags, NULL); - if (err_code != SQLITE_OK) + const char *vFs = NULL; + +#if defined(WIN32) && SQLITE_VERSION_AT_LEAST(3, 8, 1) + if (strlen(path) > 248) { - /* Save the error message before closing the SQLite handle. */ - char *msg = apr_pstrdup(scratch_pool, sqlite3_errmsg(*db3)); + WCHAR *win_path; + vFs = "win32-longpath"; /* Enable long paths in sqlite */ + + /* Long paths must be absolute */ + if (!svn_dirent_is_absolute(path)) + SVN_ERR(svn_dirent_get_absolute(&path, path, scratch_pool)); - /* We don't catch the error here, since we care more about the open - error than the close error at this point. */ - sqlite3_close(*db3); + /* Convert the path to a properly canonicalized \\?\C:\long\path */ + SVN_ERR(svn_io__utf8_to_unicode_longpath(&win_path, path, + scratch_pool)); - SQLITE_ERR_MSG(err_code, msg); + /* And convert it back to UTF-8 because there is no + sqlite3_open16_v2() yet */ + SVN_ERR(svn_utf__win32_utf16_to_utf8(&path, win_path, NULL, + scratch_pool)); } +#endif + + /* ### SQLITE_CANTOPEN */ + SQLITE_ERR_CLOSE(sqlite3_open_v2(path, &db->db3, flags, vFs), + db, scratch_pool); } } + if (timeout <= 0) + timeout = BUSY_TIMEOUT; + /* Retry until timeout when database is busy. */ - SQLITE_ERR_MSG(sqlite3_busy_timeout(*db3, BUSY_TIMEOUT), - sqlite3_errmsg(*db3)); + SQLITE_ERR_CLOSE(sqlite3_busy_timeout(db->db3, timeout), + db, scratch_pool); return SVN_NO_ERROR; } @@ -838,35 +938,29 @@ close_apr(void *data) if (db->db3 == NULL) return APR_SUCCESS; - /* Finalize any existing prepared statements. */ - for (i = 0; i < db->nbr_statements; i++) + /* Finalize any prepared statements. */ + if (db->prepared_stmts) { - if (db->prepared_stmts[i]) + for (i = 0; i < db->nbr_statements + STMT_INTERNAL_LAST; i++) { - if (db->prepared_stmts[i]->needs_reset) + if (db->prepared_stmts[i]) { + if (i < db->nbr_statements + && 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 */ + const char *stmt_text = db->statement_strings[i]; + SVN_UNUSED(stmt_text); - SVN_ERR_MALFUNCTION_NO_RETURN(); + SVN_ERR_MALFUNCTION_NO_RETURN(); #else - err = svn_error_compose_create( - err, + 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( + } + err = svn_error_compose_create( svn_sqlite__finalize(db->prepared_stmts[i]), err); + } } } @@ -888,11 +982,105 @@ close_apr(void *data) return APR_SUCCESS; } +#ifdef SVN_UNICODE_NORMALIZATION_FIXES +/* Unicode normalizing collation for WC paths */ +static int +collate_ucs_nfd(void *baton, + int len1, const void *key1, + int len2, const void *key2) +{ + svn_sqlite__db_t *db = baton; + int result; + + if (svn_utf__normcmp(key1, len1, key2, len2, + &db->sqlext_buf1, &db->sqlext_buf2, &result)) + { + /* There is really nothing we can do here if an error occurs + during Unicode normalizetion, and attempting to recover could + result in the wc.db index being corrupted. Presumably this + can only happen if the index already contains invalid UTF-8 + strings, which should never happen in any case ... */ + SVN_ERR_MALFUNCTION_NO_RETURN(); + } + + return result; +} + +static void +glob_like_ucs_nfd_common(sqlite3_context *context, + int argc, sqlite3_value **argv, + svn_boolean_t sql_like) +{ + svn_sqlite__db_t *const db = sqlite3_user_data(context); + + const char *const pattern = (void*)sqlite3_value_text(argv[0]); + const apr_size_t pattern_len = sqlite3_value_bytes(argv[0]); + const char *const string = (void*)sqlite3_value_text(argv[1]); + const apr_size_t string_len = sqlite3_value_bytes(argv[1]); + + const char *escape = NULL; + apr_size_t escape_len = 0; + + svn_boolean_t match; + svn_error_t *err; + + if (pattern_len > SQLITE_MAX_LIKE_PATTERN_LENGTH) + { + sqlite3_result_error(context, "LIKE or GLOB pattern too complex", -1); + return; + } + + if (argc == 3 && sql_like) + { + escape = (void*)sqlite3_value_text(argv[2]); + escape_len = sqlite3_value_bytes(argv[2]); + } + + if (pattern && string) + { + err = svn_utf__glob(pattern, pattern_len, string, string_len, + escape, escape_len, sql_like, + &db->sqlext_buf1, &db->sqlext_buf2, &db->sqlext_buf3, + &match); + + if (err) + { + const char *errmsg; + svn_membuf__ensure(&db->sqlext_buf1, 512); + errmsg = svn_err_best_message(err, + db->sqlext_buf1.data, + db->sqlext_buf1.size - 1); + svn_error_clear(err); + sqlite3_result_error(context, errmsg, -1); + return; + } + + sqlite3_result_int(context, match); + } +} + +/* Unicode normalizing implementation of GLOB */ +static void +glob_ucs_nfd(sqlite3_context *context, + int argc, sqlite3_value **argv) +{ + glob_like_ucs_nfd_common(context, argc, argv, FALSE); +} + +/* Unicode normalizing implementation of LIKE */ +static void +like_ucs_nfd(sqlite3_context *context, + int argc, sqlite3_value **argv) +{ + glob_like_ucs_nfd_common(context, argc, argv, TRUE); +} +#endif /* SVN_UNICODE_NORMALIZATION_FIXES */ svn_error_t * svn_sqlite__open(svn_sqlite__db_t **db, const char *path, svn_sqlite__mode_t mode, const char * const statements[], int unused1, const char * const *unused2, + apr_int32_t timeout, apr_pool_t *result_pool, apr_pool_t *scratch_pool) { SVN_ERR(svn_atomic__init_once(&sqlite_init_state, @@ -900,7 +1088,7 @@ svn_sqlite__open(svn_sqlite__db_t **db, const char *path, *db = apr_pcalloc(result_pool, sizeof(**db)); - SVN_ERR(internal_open(&(*db)->db3, path, mode, scratch_pool)); + SVN_ERR(internal_open(*db, path, mode, timeout, 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+) @@ -914,6 +1102,38 @@ svn_sqlite__open(svn_sqlite__db_t **db, const char *path, } #endif +#ifdef SVN_UNICODE_NORMALIZATION_FIXES + /* Create extension buffers with space for 200 UCS-4 characters. */ + svn_membuf__create(&(*db)->sqlext_buf1, 800, result_pool); + svn_membuf__create(&(*db)->sqlext_buf2, 800, result_pool); + svn_membuf__create(&(*db)->sqlext_buf3, 800, result_pool); + + /* Register collation and LIKE and GLOB operator replacements. */ + SQLITE_ERR_CLOSE(sqlite3_create_collation((*db)->db3, + "svn-ucs-nfd", SQLITE_UTF8, + *db, collate_ucs_nfd), + db, scratch_pool); + /* ### Is it really necessary to override these functions? + I would assume the default implementation to be collation agnostic? + And otherwise our implementation should be... + + The default implementation is in some cases index backed, while our + implementation can't be. With an index based on the collation it could + be. */ + SQLITE_ERR_CLOSE(sqlite3_create_function((*db)->db3, "glob", 2, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, + *db, glob_ucs_nfd, NULL, NULL), + db, scratch_pool); + SQLITE_ERR_CLOSE(sqlite3_create_function((*db)->db3, "like", 2, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, + *db, like_ucs_nfd, NULL, NULL), + db, scratch_pool); + SQLITE_ERR_CLOSE(sqlite3_create_function((*db)->db3, "like", 3, + SQLITE_UTF8 | SQLITE_DETERMINISTIC, + *db, like_ucs_nfd, NULL, NULL), + db, scratch_pool); +#endif /* SVN_UNICODE_NORMALIZATION_FIXES */ + #ifdef SQLITE3_DEBUG sqlite3_trace((*db)->db3, sqlite_tracer, (*db)->db3); #endif @@ -921,14 +1141,14 @@ svn_sqlite__open(svn_sqlite__db_t **db, const char *path, sqlite3_profile((*db)->db3, sqlite_profiler, (*db)->db3); #endif - /* ### simplify this. remnants of some old SQLite compat code. */ - { - int ignored_err = SQLITE_OK; - - SVN_ERR(exec_sql2(*db, "PRAGMA case_sensitive_like=1;", ignored_err)); - } - - SVN_ERR(exec_sql(*db, + SVN_ERR_CLOSE(exec_sql(*db, + /* The default behavior of the LIKE operator is to ignore case + for ASCII characters. Hence, by default 'a' LIKE 'A' is true. + The case_sensitive_like pragma installs a new application- + defined LIKE function that is either case sensitive or + insensitive depending on the value of the case_sensitive_like + pragma. */ + "PRAGMA case_sensitive_like=1;" /* Disable synchronization to disable the explicit disk flushes that make Sqlite up to 50 times slower; especially on small transactions. @@ -951,13 +1171,17 @@ svn_sqlite__open(svn_sqlite__db_t **db, const char *path, affects application(read: Subversion) performance/behavior. */ "PRAGMA foreign_keys=OFF;" /* SQLITE_DEFAULT_FOREIGN_KEYS*/ "PRAGMA locking_mode = NORMAL;" /* SQLITE_DEFAULT_LOCKING_MODE */ - )); + /* Testing shows TRUNCATE is faster than DELETE on Windows. */ + "PRAGMA journal_mode = TRUNCATE;" + ), + *db); #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;")); + SVN_ERR_CLOSE(exec_sql(*db, "PRAGMA foreign_keys=ON;"), + *db); #endif #ifdef SVN_SQLITE_REVERSE_UNORDERED_SELECTS @@ -965,7 +1189,8 @@ svn_sqlite__open(svn_sqlite__db_t **db, const char *path, 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;")); + SVN_ERR_CLOSE(exec_sql(*db, "PRAGMA reverse_unordered_selects=ON;"), + *db); #endif /* Store temporary tables in RAM instead of in temporary files, but don't @@ -1036,6 +1261,55 @@ reset_all_statements(svn_sqlite__db_t *db, return err; } +static svn_error_t * +rollback_transaction(svn_sqlite__db_t *db, + svn_error_t *error_to_wrap) +{ + svn_sqlite__stmt_t *stmt; + svn_error_t *err; + + err = get_internal_statement(&stmt, db, STMT_INTERNAL_ROLLBACK_TRANSACTION); + if (!err) + { + err = svn_error_trace(svn_sqlite__step_done(stmt)); + + if (err && err->apr_err == SVN_ERR_SQLITE_BUSY) + { + /* ### Houston, we have a problem! + + We are trying to rollback but we can't because some + statements are still busy. This leaves the database + unusable for future transactions as the current transaction + is still open. + + As we are returning the actual error as the most relevant + error in the chain, our caller might assume that it can + retry/compensate on this error (e.g. SVN_WC_LOCKED), while + in fact the SQLite database is unusable until the statements + started within this transaction are reset and the transaction + aborted. + + We try to compensate by resetting all prepared but unreset + statements; but we leave the busy error in the chain anyway to + help diagnosing the original error and help in finding where + a reset statement is missing. */ + err = svn_error_trace(reset_all_statements(db, err)); + err = svn_error_compose_create( + svn_error_trace(svn_sqlite__step_done(stmt)), + err); + } + } + + if (err) + { + /* Rollback failed, use a specific error code. */ + err = svn_error_create(SVN_SQLITE__ERR_ROLLBACK_FAILED, err, + _("SQLite transaction rollback failed")); + } + + return svn_error_compose_create(error_to_wrap, err); +} + svn_error_t * svn_sqlite__begin_transaction(svn_sqlite__db_t *db) { @@ -1078,46 +1352,37 @@ svn_sqlite__finish_transaction(svn_sqlite__db_t *db, /* Commit or rollback the sqlite transaction. */ if (err) { - 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) - { - /* ### Houston, we have a problem! - - We are trying to rollback but we can't because some - statements are still busy. This leaves the database - unusable for future transactions as the current transaction - is still open. - - As we are returning the actual error as the most relevant - error in the chain, our caller might assume that it can - retry/compensate on this error (e.g. SVN_WC_LOCKED), while - in fact the SQLite database is unusable until the statements - started within this transaction are reset and the transaction - aborted. - - We try to compensate by resetting all prepared but unreset - statements; but we leave the busy error in the chain anyway to - help diagnosing the original error and help in finding where - a reset statement is missing. */ - - err2 = reset_all_statements(db, err2); - err2 = svn_error_compose_create( - svn_sqlite__step_done(stmt), - err2); - } - - return svn_error_compose_create(err, - err2); + return svn_error_trace(rollback_transaction(db, err)); + } + else + { + err = get_internal_statement(&stmt, db, + STMT_INTERNAL_COMMIT_TRANSACTION); + if (!err) + err = svn_error_trace(svn_sqlite__step_done(stmt)); + + /* Need to rollback if the commit fails as well, because otherwise the + db connection will be left in an unusable state. + + One important case to keep in mind is trying to COMMIT with concurrent + readers. In case the commit fails, because someone else is holding a + shared lock, sqlite keeps the transaction, and *also* keeps the file + locks on the database. While the first part only prevents from using + this connection, the second part prevents everyone else from accessing + the database while the connection is open. + + See https://www.sqlite.org/lang_transaction.html + + COMMIT might also result in an SQLITE_BUSY return code if an another + thread or process has a shared lock on the database that prevented + the database from being updated. When COMMIT fails in this way, the + transaction remains active and the COMMIT can be retried later after + the reader has had a chance to clear. */ + if (err) + return svn_error_trace(rollback_transaction(db, err)); } - SVN_ERR(get_internal_statement(&stmt, db, STMT_INTERNAL_COMMIT_TRANSACTION)); - return svn_error_trace(svn_sqlite__step_done(stmt)); + return SVN_NO_ERROR; } svn_error_t * @@ -1134,18 +1399,22 @@ svn_sqlite__finish_savepoint(svn_sqlite__db_t *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. + err2 = svn_error_trace(svn_sqlite__step_done(stmt)); - ### See huge comment in svn_sqlite__finish_transaction for - further details */ + 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__finish_transaction for + further details */ - err2 = reset_all_statements(db, err2); - err2 = svn_error_compose_create(svn_sqlite__step_done(stmt), err2); + err2 = svn_error_trace(reset_all_statements(db, err2)); + err2 = svn_error_compose_create( + svn_error_trace(svn_sqlite__step_done(stmt)), + err2); + } } err = svn_error_compose_create(err, err2); @@ -1153,9 +1422,9 @@ svn_sqlite__finish_savepoint(svn_sqlite__db_t *db, STMT_INTERNAL_RELEASE_SAVEPOINT_SVN); if (!err2) - err2 = svn_sqlite__step_done(stmt); + err2 = svn_error_trace(svn_sqlite__step_done(stmt)); - return svn_error_trace(svn_error_compose_create(err, err2)); + return svn_error_compose_create(err, err2); } SVN_ERR(get_internal_statement(&stmt, db, @@ -1203,7 +1472,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, - NULL, 0, NULL, + NULL, 0, NULL, 0, scratch_pool, scratch_pool)); { @@ -1212,7 +1481,7 @@ svn_sqlite__hotcopy(const char *src_path, int rc1, rc2; SVN_ERR(svn_sqlite__open(&dst_db, dst_path, svn_sqlite__mode_rwcreate, - NULL, 0, NULL, scratch_pool, scratch_pool)); + NULL, 0, NULL, 0, scratch_pool, scratch_pool)); backup = sqlite3_backup_init(dst_db->db3, "main", src_db->db3, "main"); if (!backup) return svn_error_createf(SVN_ERR_SQLITE_ERROR, NULL, @@ -1244,6 +1513,8 @@ svn_sqlite__hotcopy(const char *src_path, SVN_ERR(svn_sqlite__close(src_db)); + SVN_ERR(svn_io_copy_perms(src_path, dst_path, scratch_pool)); + return SVN_NO_ERROR; } @@ -1251,8 +1522,6 @@ struct function_wrapper_baton_t { svn_sqlite__func_t func; void *baton; - - apr_pool_t *scratch_pool; }; static void @@ -1262,22 +1531,12 @@ wrapped_func(sqlite3_context *context, { struct function_wrapper_baton_t *fwb = sqlite3_user_data(context); svn_sqlite__context_t sctx; - svn_sqlite__value_t **local_vals = - apr_palloc(fwb->scratch_pool, - sizeof(svn_sqlite__value_t *) * argc); svn_error_t *err; - int i; + void *void_values = values; sctx.context = context; - for (i = 0; i < argc; i++) - { - local_vals[i] = apr_palloc(fwb->scratch_pool, sizeof(*local_vals[i])); - local_vals[i]->value = values[i]; - } - - err = fwb->func(&sctx, argc, local_vals, fwb->scratch_pool); - svn_pool_clear(fwb->scratch_pool); + err = fwb->func(&sctx, argc, void_values, fwb->baton); if (err) { @@ -1289,21 +1548,27 @@ wrapped_func(sqlite3_context *context, } } + svn_error_t * svn_sqlite__create_scalar_function(svn_sqlite__db_t *db, const char *func_name, int argc, + svn_boolean_t deterministic, svn_sqlite__func_t func, void *baton) { + int eTextRep; struct function_wrapper_baton_t *fwb = apr_pcalloc(db->state_pool, sizeof(*fwb)); - fwb->scratch_pool = svn_pool_create(db->state_pool); fwb->func = func; fwb->baton = baton; - SQLITE_ERR(sqlite3_create_function(db->db3, func_name, argc, SQLITE_ANY, + eTextRep = SQLITE_ANY; + if (deterministic) + eTextRep |= SQLITE_DETERMINISTIC; + + SQLITE_ERR(sqlite3_create_function(db->db3, func_name, argc, eTextRep, fwb, wrapped_func, NULL, NULL), db); @@ -1313,13 +1578,15 @@ svn_sqlite__create_scalar_function(svn_sqlite__db_t *db, int svn_sqlite__value_type(svn_sqlite__value_t *val) { - return sqlite3_value_type(val->value); + void *v = val; + return sqlite3_value_type(v); } const char * svn_sqlite__value_text(svn_sqlite__value_t *val) { - return (const char *) sqlite3_value_text(val->value); + void *v = val; + return (const char *) sqlite3_value_text(v); } void @@ -1333,3 +1600,9 @@ svn_sqlite__result_int64(svn_sqlite__context_t *sctx, apr_int64_t val) { sqlite3_result_int64(sctx->context, val); } + +void +svn_sqlite__result_error(svn_sqlite__context_t *sctx, const char *msg, int num) +{ + sqlite3_result_error(sctx->context, msg, num); +} |