summaryrefslogtreecommitdiff
path: root/subversion/libsvn_subr/sqlite.c
diff options
context:
space:
mode:
Diffstat (limited to 'subversion/libsvn_subr/sqlite.c')
-rw-r--r--subversion/libsvn_subr/sqlite.c515
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);
+}