diff options
Diffstat (limited to 'subversion/tests/libsvn_wc/wc-queries-test.c')
-rw-r--r-- | subversion/tests/libsvn_wc/wc-queries-test.c | 223 |
1 files changed, 198 insertions, 25 deletions
diff --git a/subversion/tests/libsvn_wc/wc-queries-test.c b/subversion/tests/libsvn_wc/wc-queries-test.c index 0621720..d63aa57 100644 --- a/subversion/tests/libsvn_wc/wc-queries-test.c +++ b/subversion/tests/libsvn_wc/wc-queries-test.c @@ -22,6 +22,7 @@ */ #include "svn_pools.h" +#include "svn_hash.h" #include "svn_ctype.h" #include "private/svn_dep_compat.h" @@ -30,22 +31,17 @@ #include "../svn_test.h" #ifdef SVN_SQLITE_INLINE -/* Include sqlite3 inline, making all symbols private. */ - #define SQLITE_API static - #ifdef __APPLE__ - #include <Availability.h> - #if __MAC_OS_X_VERSION_MIN_REQUIRED < 1060 - /* <libkern/OSAtomic.h> is included on OS X by sqlite3.c, and - on old systems (Leopard or older), it cannot be compiled - with -std=c89 because it uses inline. This is a work-around. */ - #define inline __inline__ - #include <libkern/OSAtomic.h> - #undef inline - #endif - #endif - #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 #include "../../libsvn_wc/wc-queries.h" @@ -56,7 +52,7 @@ WC_QUERIES_SQL_DECLARE_STATEMENT_INFO(wc_query_info); /* The first query after the normal wc queries */ #define STMT_SCHEMA_FIRST STMT_CREATE_SCHEMA -#define SQLITE_ERR(x) \ +#define SQLITE_ERR(x) do \ { \ int sqlite_err__temp = (x); \ if (sqlite_err__temp != SQLITE_OK) \ @@ -101,6 +97,7 @@ static const int slow_statements[] = /* Full temporary table read */ STMT_INSERT_ACTUAL_EMPTIES, + STMT_INSERT_ACTUAL_EMPTIES_FILES, STMT_SELECT_REVERT_LIST_RECURSIVE, STMT_SELECT_DELETE_LIST, STMT_SELECT_UPDATE_MOVE_LIST, @@ -177,15 +174,15 @@ create_memory_db(sqlite3 **db, static svn_error_t * test_sqlite_version(apr_pool_t *scratch_pool) { - printf("DBG: Using Sqlite %s\n", sqlite3_version); + printf("DBG: Using Sqlite %s\n", sqlite3_libversion()); if (sqlite3_libversion_number() != SQLITE_VERSION_NUMBER) - printf("DBG: Compiled against Sqlite %s", SQLITE_VERSION); + printf("DBG: Compiled against Sqlite %s\n", SQLITE_VERSION); if (sqlite3_libversion_number() < SQLITE_VERSION_NUMBER) return svn_error_createf(SVN_ERR_TEST_FAILED, NULL, "Compiled against Sqlite %s (at runtime we have Sqlite %s)", - SQLITE_VERSION, sqlite3_version); + SQLITE_VERSION, sqlite3_libversion()); #if !SQLITE_VERSION_AT_LEAST(3, 7, 9) return svn_error_create(SVN_ERR_TEST_FAILED, NULL, @@ -307,14 +304,21 @@ parse_explanation_item(struct explanation_item **parsed_item, item->search = TRUE; /* Search or scan */ token = apr_strtok(NULL, " ", &last); - if (!MATCH_TOKEN(token, "TABLE")) + if (MATCH_TOKEN(token, "TABLE")) + { + item->table = apr_strtok(NULL, " ", &last); + } + else if (MATCH_TOKEN(token, "SUBQUERY")) + { + item->table = apr_psprintf(result_pool, "SUBQUERY-%s", + apr_strtok(NULL, " ", &last)); + } + else { printf("DBG: Expected 'TABLE', got '%s' in '%s'\n", token, text); return SVN_NO_ERROR; /* Nothing to parse */ } - item->table = apr_strtok(NULL, " ", &last); - token = apr_strtok(NULL, " ", &last); /* Skip alias */ @@ -418,7 +422,7 @@ parse_explanation_item(struct explanation_item **parsed_item, return SVN_NO_ERROR; } - /* Parsing successfull */ + /* Parsing successful */ } else if (MATCH_TOKEN(item->operation, "EXECUTE")) { @@ -606,7 +610,7 @@ test_query_expectations(apr_pool_t *scratch_pool) apr_pstrcat(iterpool, "EXPLAIN QUERY PLAN ", wc_queries[i], - NULL), + SVN_VA_NULL), -1, &stmt, &tail); if (r != SQLITE_OK) @@ -744,6 +748,105 @@ test_query_expectations(apr_pool_t *scratch_pool) return warnings; } +static svn_error_t * +test_query_duplicates(apr_pool_t *scratch_pool) +{ + sqlite3 *sdb; + int i; + apr_pool_t *iterpool = svn_pool_create(scratch_pool); + svn_error_t *warnings = NULL; + svn_boolean_t supports_query_info; + apr_hash_t *sha_to_query = apr_hash_make(scratch_pool); + + SVN_ERR(create_memory_db(&sdb, scratch_pool)); + + SVN_ERR(supported_explain_query_plan(&supports_query_info, sdb, + scratch_pool)); + if (!supports_query_info) + { + SQLITE_ERR(sqlite3_close(sdb)); + return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL, + "Sqlite doesn't support EXPLAIN QUERY PLAN"); + } + + for (i = 0; i < STMT_SCHEMA_FIRST; i++) + { + sqlite3_stmt *stmt; + const char *tail; + int r; + svn_stringbuf_t *result; + svn_checksum_t *checksum; + + if (is_schema_statement(i)) + continue; + + /* Prepare statement to find if it is a single statement. */ + r = sqlite3_prepare_v2(sdb, wc_queries[i], -1, &stmt, &tail); + + if (r != SQLITE_OK) + continue; /* Parse failure is already reported by 'test_parable' */ + + SQLITE_ERR(sqlite3_finalize(stmt)); + if (tail[0] != '\0') + continue; /* Multi-queries are currently not testable */ + + svn_pool_clear(iterpool); + + r = sqlite3_prepare_v2(sdb, + apr_pstrcat(iterpool, + "EXPLAIN ", + wc_queries[i], + SVN_VA_NULL), + -1, &stmt, &tail); + + if (r != SQLITE_OK) + continue; /* EXPLAIN not enabled or doesn't support this query */ + + result = svn_stringbuf_create_empty(iterpool); + + while (SQLITE_ROW == (r = sqlite3_step(stmt))) + { + int col; + + for (col = 0; col < sqlite3_column_count(stmt); col++) + { + const char *txt = (const char*)sqlite3_column_text(stmt, col); + if (txt) + svn_stringbuf_appendcstr(result, txt); + + svn_stringbuf_appendcstr(result, "|"); + } + + svn_stringbuf_appendcstr(result, "\n"); + } + + SQLITE_ERR(sqlite3_reset(stmt)); + SQLITE_ERR(sqlite3_finalize(stmt)); + + SVN_ERR(svn_checksum(&checksum, svn_checksum_sha1, + result->data, result->len, + iterpool)); + + { + const char *hex = svn_checksum_to_cstring(checksum, scratch_pool); + const char *other; + + other = svn_hash_gets(sha_to_query, hex); + if (other) + { + warnings = svn_error_createf(SVN_ERR_TEST_FAILED, warnings, + "Query %s has an identical execution plan as %s", + wc_query_info[i][0], other); + } + else + svn_hash_sets(sha_to_query, hex, wc_query_info[i][0]); + } + } + SQLITE_ERR(sqlite3_close(sdb)); /* Close the DB if ok; otherwise leaked */ + + return warnings; +} + /* Helper to verify a bit of data in the sqlite3 statistics */ static int parse_stat_data(const char *stat) @@ -824,6 +927,15 @@ test_schema_statistics(apr_pool_t *scratch_pool) "VALUES (1, '', '')", NULL, NULL, NULL)); + SQLITE_ERR( + sqlite3_exec(sdb, + "INSERT INTO EXTERNALS (wc_id, local_relpath," + " parent_relpath, repos_id," + " presence, kind, def_local_relpath," + " def_repos_relpath) " + "VALUES (1, 'subdir', '', 1, 'normal', 'dir', '', '')", + NULL, NULL, NULL)); + /* These are currently not necessary for query optimization, but it's better to tell Sqlite how we intend to use this table anyway */ SQLITE_ERR( @@ -882,7 +994,62 @@ test_schema_statistics(apr_pool_t *scratch_pool) return SVN_NO_ERROR; } -struct svn_test_descriptor_t test_funcs[] = +/* An SQLite application defined function that allows SQL queries to + use "relpath_depth(local_relpath)". */ +static void relpath_depth_sqlite(sqlite3_context* context, + int argc, + sqlite3_value* values[]) +{ + SVN_ERR_MALFUNCTION_NO_RETURN(); /* STUB! */ +} + +/* Parse all verify/check queries */ +static svn_error_t * +test_verify_parsable(apr_pool_t *scratch_pool) +{ + sqlite3 *sdb; + int i; + + SVN_ERR(create_memory_db(&sdb, scratch_pool)); + + SQLITE_ERR(sqlite3_create_function(sdb, "relpath_depth", 1, SQLITE_ANY, NULL, + relpath_depth_sqlite, NULL, NULL)); + + for (i=STMT_VERIFICATION_TRIGGERS; wc_queries[i]; i++) + { + sqlite3_stmt *stmt; + const char *text = wc_queries[i]; + + /* Some of our statement texts contain multiple queries. We prepare + them all. */ + while (*text != '\0') + { + const char *tail; + int r = sqlite3_prepare_v2(sdb, text, -1, &stmt, &tail); + + if (r != SQLITE_OK) + return svn_error_createf(SVN_ERR_SQLITE_ERROR, NULL, + "Preparing %s failed: %s\n%s", + wc_query_info[i][0], + sqlite3_errmsg(sdb), + text); + + SQLITE_ERR(sqlite3_finalize(stmt)); + + /* Continue after the current statement */ + text = tail; + } + } + + SQLITE_ERR(sqlite3_close(sdb)); /* Close the DB if ok; otherwise leaked */ + + return SVN_NO_ERROR; +} + + +static int max_threads = 1; + +static struct svn_test_descriptor_t test_funcs[] = { SVN_TEST_NULL, SVN_TEST_PASS2(test_sqlite_version, @@ -891,7 +1058,13 @@ struct svn_test_descriptor_t test_funcs[] = "queries are parsable"), SVN_TEST_PASS2(test_query_expectations, "test query expectations"), + SVN_TEST_PASS2(test_query_duplicates, + "test query duplicates"), SVN_TEST_PASS2(test_schema_statistics, "test schema statistics"), + SVN_TEST_PASS2(test_verify_parsable, + "verify queries are parsable"), SVN_TEST_NULL }; + +SVN_TEST_MAIN |