Index: subversion/libsvn_wc/props.c
===================================================================
--- subversion/libsvn_wc/props.c (revision 1067790)
+++ subversion/libsvn_wc/props.c (working copy)
@@ -1711,6 +1711,7 @@
SVN_ERR(svn_wc__db_read_props_of_immediates(b->db, local_abspath,
b->receiver_func,
b->receiver_baton,
+ NULL, NULL,
scratch_pool));
return SVN_NO_ERROR;
}
@@ -1725,47 +1726,42 @@
void *cancel_baton,
apr_pool_t *scratch_pool)
{
- struct read_dir_props_baton read_dir_baton;
+ apr_hash_t *props;
- if (depth <= svn_depth_immediates)
- {
- apr_hash_t *props;
+ SVN_ERR(svn_wc__db_read_props(&props, wc_ctx->db, local_abspath,
+ scratch_pool, scratch_pool));
+ if (receiver_func && props && apr_hash_count(props) > 0)
+ SVN_ERR((*receiver_func)(receiver_baton, local_abspath, props,
+ scratch_pool));
+ if (depth == svn_depth_empty)
+ return SVN_NO_ERROR;
- SVN_ERR(svn_wc__db_read_props(&props, wc_ctx->db, local_abspath,
- scratch_pool, scratch_pool));
- if (receiver_func && props && apr_hash_count(props) > 0)
- SVN_ERR((*receiver_func)(receiver_baton, local_abspath, props,
- scratch_pool));
- if (depth == svn_depth_empty)
- return SVN_NO_ERROR;
- }
-
if (depth == svn_depth_files)
{
SVN_ERR(svn_wc__db_read_props_of_files(wc_ctx->db, local_abspath,
receiver_func, receiver_baton,
+ cancel_func, cancel_baton,
scratch_pool));
- return SVN_NO_ERROR;
}
-
- if (depth == svn_depth_immediates)
+ else if (depth == svn_depth_immediates)
{
SVN_ERR(svn_wc__db_read_props_of_immediates(wc_ctx->db, local_abspath,
- receiver_func,
- receiver_baton,
+ receiver_func, receiver_baton,
+ cancel_func, cancel_baton,
scratch_pool));
- return SVN_NO_ERROR;
}
+ else if (depth == svn_depth_infinity)
+ {
+ SVN_ERR(svn_wc__db_read_props_recursive(wc_ctx->db, local_abspath,
+ receiver_func, receiver_baton,
+ cancel_func, cancel_baton,
+ scratch_pool));
+ }
+ else
+ {
+ SVN_ERR_MALFUNCTION();
+ }
- read_dir_baton.db = wc_ctx->db;
- read_dir_baton.root_abspath = local_abspath;
- read_dir_baton.receiver_func = receiver_func;
- read_dir_baton.receiver_baton = receiver_baton;
-
- SVN_ERR(svn_wc__internal_walk_children(wc_ctx->db, local_abspath, FALSE,
- read_dir_props, &read_dir_baton,
- depth, cancel_func, cancel_baton,
- scratch_pool));
return SVN_NO_ERROR;
}
Index: subversion/libsvn_wc/wc-queries.sql
===================================================================
--- subversion/libsvn_wc/wc-queries.sql (revision 1067790)
+++ subversion/libsvn_wc/wc-queries.sql (working copy)
@@ -726,7 +726,69 @@
UPDATE nodes SET checksum = ?4
WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3;
+/* ------------------------------------------------------------------------- */
+/* PROOF OF CONCEPT: Complex queries for callback walks, using file-backed
+ * temporary database. */
+-- STMT_ATTACH_TEMPORARY_DATABASE
+ATTACH DATABASE '' AS temp_query_cache;
+
+-- STMT_DETACH_TEMPORARY_DATABASE
+DETACH DATABASE temp_query_cache;
+
+-- STMT_CLEAR_NODE_PROPS_CACHE
+DROP TABLE IF EXISTS temp_query_cache.node_props_cache;
+
+-- STMT_QUERY_NODE_PROPS_OF_CHILDREN
+CREATE TABLE temp_query_cache.node_props_cache AS
+ SELECT N.local_relpath, N.properties, N.kind, ?1 AS wc_id
+ FROM nodes AS N
+ JOIN (
+ SELECT local_relpath, MAX(op_depth) AS op_depth FROM nodes
+ WHERE wc_id = ?1 AND parent_relpath = ?2
+ GROUP BY local_relpath
+ ) AS V
+ ON N.local_relpath = V.local_relpath
+ AND N.op_depth = V.op_depth
+ WHERE N.wc_id = ?1
+ AND (N.presence = 'normal' OR N.presence = 'incomplete');
+CREATE UNIQUE INDEX temp_query_cache.node_props_cache_unique
+ ON node_props_cache (local_relpath);
+
+-- STMT_QUERY_NODE_PROPS_RECURSIVE
+CREATE TABLE temp_query_cache.node_props_cache AS
+ SELECT N.local_relpath, N.properties, '' AS kind, ?1 AS wc_id
+ FROM nodes AS N
+ JOIN (
+ SELECT local_relpath, MAX(op_depth) AS op_depth FROM nodes
+ WHERE wc_id = ?1
+ AND local_relpath LIKE
+ CASE ?2 WHEN '' THEN '%' ELSE ?2 || '/%' END
+ GROUP BY local_relpath
+ ) AS V
+ ON N.local_relpath = V.local_relpath
+ AND N.op_depth = V.op_depth
+ WHERE N.wc_id = ?1
+ AND (N.presence = 'normal' OR N.presence = 'incomplete');
+CREATE UNIQUE INDEX temp_query_cache.node_props_cache_unique
+ ON node_props_cache (local_relpath);
+
+-- STMT_REPLACE_ACTUAL_PROPS_IN_CACHE
+INSERT OR REPLACE INTO temp_query_cache.node_props_cache
+ (local_relpath, properties)
+ SELECT N.local_relpath, N.properties
+ FROM actual_node AS N JOIN temp_query_cache.node_props_cache AS C
+ ON N.local_relpath = C.local_relpath
+ AND N.wc_id = C.wc_id;
+/*COMMIT TRANSACTION;*/
+
+-- STMT_SELECT_RELEVANT_PROPS_FROM_CACHE
+SELECT local_relpath, properties, kind
+ FROM temp_query_cache.node_props_cache
+WHERE properties IS NOT NULL
+ORDER BY local_relpath;
+
+
/* ------------------------------------------------------------------------- */
/* Grab all the statements related to the schema. */
Index: subversion/libsvn_wc/wc_db.c
===================================================================
--- subversion/libsvn_wc/wc_db.c (revision 1067790)
+++ subversion/libsvn_wc/wc_db.c (working copy)
@@ -5056,187 +5056,150 @@
return SVN_NO_ERROR;
}
-/* Parse a node's PROP_DATA (which is PROP_DATA_LEN bytes long)
- * into a hash table keyed by property names and containing property values.
- *
- * If parsing succeeds, and the set of properties is not empty,
- * add the hash table to PROPS_PER_CHILD, keyed by the absolute path
- * of the node CHILD_RELPATH within the working copy at WCROOT_ABSPATH.
- *
- * If the set of properties is empty, and PROPS_PER_CHILD already contains
- * an entry for the node, clear the entry. This facilitates overriding
- * properties retrieved from the NODES table with empty sets of properties
- * stored in the ACTUAL_NODE table. */
+/** POC **/
+
static svn_error_t *
-maybe_add_child_props(apr_hash_t *props_per_child,
- const char *prop_data,
- apr_size_t prop_data_len,
- const char *child_relpath,
- const char *wcroot_abspath,
- apr_pool_t *result_pool,
- apr_pool_t *scratch_pool)
+attach_cache_db(svn_wc__db_pdh_t *pdh)
{
- const char *child_abspath;
- apr_hash_t *props;
- svn_skel_t *prop_skel;
+ return svn_error_return
+ (svn_sqlite__exec_statements(pdh->wcroot->sdb,
+ STMT_ATTACH_TEMPORARY_DATABASE));
+}
- prop_skel = svn_skel__parse(prop_data, prop_data_len, scratch_pool);
- if (svn_skel__list_length(prop_skel) == 0)
- return SVN_NO_ERROR;
+static svn_error_t *
+detach_cache_db(svn_wc__db_pdh_t *pdh)
+{
+ return svn_error_return
+ (svn_sqlite__exec_statements(pdh->wcroot->sdb,
+ STMT_DETACH_TEMPORARY_DATABASE));
+}
- child_abspath = svn_dirent_join(wcroot_abspath, child_relpath, result_pool);
- SVN_ERR(svn_skel__parse_proplist(&props, prop_skel, result_pool));
- if (apr_hash_count(props))
- apr_hash_set(props_per_child, child_abspath, APR_HASH_KEY_STRING, props);
- else
- apr_hash_set(props_per_child, child_abspath, APR_HASH_KEY_STRING, NULL);
+typedef struct cache_props_of_children_baton_t {
+ int stmt_idx;
+ apr_int64_t wc_id;
+ const char *local_relpath;
+} cache_props_of_children_baton_t;
+static svn_error_t *
+cache_props_of_children(void *cb_baton,
+ svn_sqlite__db_t *db,
+ apr_pool_t *scratch_pool)
+{
+ cache_props_of_children_baton_t *baton = cb_baton;
+ svn_sqlite__stmt_t *stmt;
+
+ SVN_ERR(svn_sqlite__get_statement(&stmt, db, baton->stmt_idx));
+ SVN_ERR(svn_sqlite__bindf(stmt, "is", baton->wc_id, baton->local_relpath));
+ SVN_ERR(svn_sqlite__step_done(stmt));
+ SVN_ERR(svn_sqlite__exec_statements(db, STMT_REPLACE_ACTUAL_PROPS_IN_CACHE));
+
return SVN_NO_ERROR;
}
/* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
* a hash table mapping char * names onto svn_string_t *
- * values for any properties of immediate child nodes of LOCAL_ABSPATH.
+ * values for any properties of immediate or recursive child nodes of
+ * LOCAL_ABSPATH, the actual query being determined by STMT_IDX.
* If FILES_ONLY is true, only report properties for file child nodes.
+ * Check for cancellation between calls of RECEIVER_FUNC.
*/
+
static svn_error_t *
read_props_of_children(svn_wc__db_t *db,
+ int stmt_idx,
const char *local_abspath,
svn_boolean_t files_only,
svn_wc__proplist_receiver_t receiver_func,
void *receiver_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
apr_pool_t *scratch_pool)
{
svn_wc__db_pdh_t *pdh;
- const char *local_relpath;
- const char *prev_child_relpath;
+ cache_props_of_children_baton_t txn_baton;
svn_sqlite__stmt_t *stmt;
svn_boolean_t have_row;
- apr_hash_t *props_per_child;
- apr_hash_t *files;
- apr_hash_t *not_present;
- apr_hash_index_t *hi;
apr_pool_t *iterpool;
SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
SVN_ERR_ASSERT(receiver_func);
- SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &local_relpath, db,
- local_abspath,
+ SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &txn_baton.local_relpath,
+ db, local_abspath,
svn_sqlite__mode_readwrite,
scratch_pool, scratch_pool));
VERIFY_USABLE_PDH(pdh);
- props_per_child = apr_hash_make(scratch_pool);
- not_present = apr_hash_make(scratch_pool);
- if (files_only)
- files = apr_hash_make(scratch_pool);
- else
- files = NULL;
+ SVN_ERR(attach_cache_db(pdh));
+ SVN_ERR(svn_sqlite__exec_statements(pdh->wcroot->sdb,
+ STMT_CLEAR_NODE_PROPS_CACHE));
+ txn_baton.stmt_idx = stmt_idx;
+ txn_baton.wc_id = pdh->wcroot->wc_id;
+ SVN_ERR(svn_sqlite__with_transaction(pdh->wcroot->sdb,
+ cache_props_of_children,
+ &txn_baton,
+ scratch_pool));
+
+ iterpool = svn_pool_create(scratch_pool);
+
SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
- STMT_SELECT_NODE_PROPS_OF_CHILDREN));
- SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath));
+ STMT_SELECT_RELEVANT_PROPS_FROM_CACHE));
SVN_ERR(svn_sqlite__step(&have_row, stmt));
- prev_child_relpath = NULL;
while (have_row)
{
- svn_wc__db_status_t child_presence;
- const char *child_relpath;
const char *prop_data;
apr_size_t len;
- child_relpath = svn_sqlite__column_text(stmt, 2, scratch_pool);
-
- if (prev_child_relpath && strcmp(child_relpath, prev_child_relpath) == 0)
+ if (files_only)
{
- /* Same child, but lower op_depth -- skip this row. */
- SVN_ERR(svn_sqlite__step(&have_row, stmt));
- continue;
- }
- prev_child_relpath = child_relpath;
+ svn_wc__db_kind_t child_kind;
- child_presence = svn_sqlite__column_token(stmt, 1, presence_map);
- if (child_presence != svn_wc__db_status_normal)
- {
- apr_hash_set(not_present, child_relpath, APR_HASH_KEY_STRING, "");
- SVN_ERR(svn_sqlite__step(&have_row, stmt));
- continue;
- }
-
- prop_data = svn_sqlite__column_blob(stmt, 0, &len, NULL);
- if (prop_data)
- {
- if (files_only)
+ child_kind = svn_sqlite__column_token(stmt, 2, kind_map);
+ if (child_kind != svn_wc__db_kind_file &&
+ child_kind != svn_wc__db_kind_symlink)
{
- svn_wc__db_kind_t child_kind;
-
- child_kind = svn_sqlite__column_token(stmt, 3, kind_map);
- if (child_kind != svn_wc__db_kind_file &&
- child_kind != svn_wc__db_kind_symlink)
- {
- SVN_ERR(svn_sqlite__step(&have_row, stmt));
- continue;
- }
- apr_hash_set(files, child_relpath, APR_HASH_KEY_STRING, NULL);
+ SVN_ERR(svn_sqlite__step(&have_row, stmt));
+ continue;
}
-
- SVN_ERR(maybe_add_child_props(props_per_child, prop_data, len,
- child_relpath, pdh->wcroot->abspath,
- scratch_pool, scratch_pool));
}
- SVN_ERR(svn_sqlite__step(&have_row, stmt));
- }
+ svn_pool_clear(iterpool);
- SVN_ERR(svn_sqlite__reset(stmt));
+ /* See if someone wants to cancel this operation. */
+ if (cancel_func)
+ SVN_ERR(cancel_func(cancel_baton));
- SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
- STMT_SELECT_ACTUAL_PROPS_OF_CHILDREN));
- SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath));
- SVN_ERR(svn_sqlite__step(&have_row, stmt));
- while (have_row)
- {
- const char *child_relpath;
- const char *prop_data;
- apr_size_t len;
-
- prop_data = svn_sqlite__column_blob(stmt, 0, &len, NULL);
+ prop_data = svn_sqlite__column_blob(stmt, 1, &len, NULL);
if (prop_data)
{
- child_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
+ svn_skel_t *prop_skel;
- if (apr_hash_get(not_present, child_relpath, APR_HASH_KEY_STRING) ||
- (files_only &&
- apr_hash_get(files, child_relpath, APR_HASH_KEY_STRING) == NULL))
+ prop_skel = svn_skel__parse(prop_data, len, iterpool);
+ if (svn_skel__list_length(prop_skel) != 0)
{
- SVN_ERR(svn_sqlite__step(&have_row, stmt));
- continue;
+ const char *child_relpath;
+ const char *child_abspath;
+ apr_hash_t *props;
+
+ child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
+ child_abspath = svn_dirent_join(pdh->wcroot->abspath,
+ child_relpath, iterpool);
+ SVN_ERR(svn_skel__parse_proplist(&props, prop_skel, iterpool));
+ if (receiver_func && apr_hash_count(props) > 0)
+ {
+ SVN_ERR((*receiver_func)(receiver_baton,
+ child_abspath, props,
+ iterpool));
+ }
}
- SVN_ERR(maybe_add_child_props(props_per_child, prop_data, len,
- child_relpath, pdh->wcroot->abspath,
- scratch_pool, scratch_pool));
}
SVN_ERR(svn_sqlite__step(&have_row, stmt));
}
SVN_ERR(svn_sqlite__reset(stmt));
-
- iterpool = svn_pool_create(scratch_pool);
- for (hi = apr_hash_first(scratch_pool, props_per_child);
- hi;
- hi = apr_hash_next(hi))
- {
- const char *child_abspath = svn__apr_hash_index_key(hi);
- apr_hash_t *child_props = svn__apr_hash_index_val(hi);
-
- svn_pool_clear(iterpool);
-
- if (child_props)
- SVN_ERR((*receiver_func)(receiver_baton, child_abspath, child_props,
- iterpool));
- }
+ SVN_ERR(detach_cache_db(pdh));
svn_pool_destroy(iterpool);
return SVN_NO_ERROR;
@@ -5247,11 +5210,17 @@
const char *local_abspath,
svn_wc__proplist_receiver_t receiver_func,
void *receiver_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
apr_pool_t *scratch_pool)
{
- return svn_error_return(read_props_of_children(db, local_abspath, TRUE,
- receiver_func, receiver_baton,
- scratch_pool));
+ SVN_ERR(read_props_of_children(db,
+ STMT_QUERY_NODE_PROPS_OF_CHILDREN,
+ local_abspath, TRUE,
+ receiver_func, receiver_baton,
+ cancel_func, cancel_baton,
+ scratch_pool));
+ return SVN_NO_ERROR;
}
svn_error_t *
@@ -5259,13 +5228,36 @@
const char *local_abspath,
svn_wc__proplist_receiver_t receiver_func,
void *receiver_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
apr_pool_t *scratch_pool)
{
- return svn_error_return(read_props_of_children(db, local_abspath, FALSE,
- receiver_func, receiver_baton,
- scratch_pool));
+ SVN_ERR(read_props_of_children(db,
+ STMT_QUERY_NODE_PROPS_OF_CHILDREN,
+ local_abspath, FALSE,
+ receiver_func, receiver_baton,
+ cancel_func, cancel_baton,
+ scratch_pool));
+ return SVN_NO_ERROR;
}
+svn_error_t *
+svn_wc__db_read_props_recursive(svn_wc__db_t *db,
+ const char *local_abspath,
+ svn_wc__proplist_receiver_t receiver_func,
+ void *receiver_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool)
+{
+ SVN_ERR(read_props_of_children(db,
+ STMT_QUERY_NODE_PROPS_RECURSIVE,
+ local_abspath, FALSE,
+ receiver_func, receiver_baton,
+ cancel_func, cancel_baton,
+ scratch_pool));
+ return SVN_NO_ERROR;
+}
static svn_error_t *
db_read_pristine_props(apr_hash_t **props,
Index: subversion/libsvn_wc/wc_db.h
===================================================================
--- subversion/libsvn_wc/wc_db.h (revision 1067790)
+++ subversion/libsvn_wc/wc_db.h (working copy)
@@ -1580,9 +1580,10 @@
svn_error_t *
svn_wc__db_read_props_of_files(svn_wc__db_t *db,
const char *local_abspath,
- svn_wc__proplist_receiver_t
- receiver_func,
+ svn_wc__proplist_receiver_t receiver_func,
void *receiver_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
apr_pool_t *scratch_pool);
/* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
@@ -1592,11 +1593,25 @@
svn_error_t *
svn_wc__db_read_props_of_immediates(svn_wc__db_t *db,
const char *local_abspath,
- svn_wc__proplist_receiver_t
- receiver_func,
+ svn_wc__proplist_receiver_t receiver_func,
void *receiver_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
apr_pool_t *scratch_pool);
+/* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
+ * a hash table mapping char * names onto svn_string_t *
+ * values for any properties of all (recursive) child nodes of LOCAL_ABSPATH.
+ */
+svn_error_t *
+svn_wc__db_read_props_recursive(svn_wc__db_t *db,
+ const char *local_abspath,
+ svn_wc__proplist_receiver_t receiver_func,
+ void *receiver_baton,
+ svn_cancel_func_t cancel_func,
+ void *cancel_baton,
+ apr_pool_t *scratch_pool);
+
/* Set *PROPS to the properties of the node LOCAL_ABSPATH in the WORKING
tree (looking through to the BASE tree as required).