On 07.02.2011 21:57, Stefan Sperling wrote:
> On Mon, Feb 07, 2011 at 09:32:48PM +0100, Branko Čibej wrote:
>> On 07.02.2011 17:10, Stefan Sperling wrote:
>>> The bug is probabaly in the following query.
>>> Maybe the INSERT OR REPLACE doesn't work as intended?
>>> And why is COMMIT TRANSACTION commented, BTW? Is this the problem?
>>>
>>> -- 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;
>> Yes, this query that handles actual_nodes is bogus. I've been looking at
>> fixing it, will update the patch when I find a solution.
>>
>> Or if you like, I can just commit it -- but that'd be throwing code over
>> my shoulder, as there's little chance that I'd have time to maintain
>> such a thing.
> If you prefer, post the patch and I will make sure to understand
> it sufficiently enough to be able to maintain it when it gets committed.

Well, here it is, I fixed the thinko in the actual_props query and got
all prop_tests to pass with this version. Did I say that the way
ACTUAL_NODE is separate from NODE makes these kinds of WC operations
(that merge results from both tables) quite complex and expensive?

There are other differences from the previous patch:

    * I use a plain temporary table for the query cache instead of a
      temporary database. It turns out that the temp_store pragma
      affects even nominally file-backed temporary databases, so there's
      no actual difference when using one or the other.
    * I introduced a new view to the wc_db schema, called NODES_CURRENT,
      which represents a query on NODES that filters out the duplicates
      by keeping only the version of each (wc_id, local_relpath) with
      the highest op_depth. There are any number of WC operations that
      do essentially the same thing in C code, so using this view in the
      related SQL queries could simplify the code that processes the
      results quite a bit. The addition of this view requires a WC
      version bump, also in the patch.

-- Brane
Index: subversion/libsvn_wc/props.c
===================================================================
--- subversion/libsvn_wc/props.c        (revision 1068618)
+++ 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,41 @@
                             void *cancel_baton,
                             apr_pool_t *scratch_pool)
 {
-  struct read_dir_props_baton read_dir_baton;
-
-  if (depth <= svn_depth_immediates)
+  switch (depth)
     {
-      apr_hash_t *props;
+    case svn_depth_empty:
+      {
+        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;
-    }
-
-  if (depth == svn_depth_files)
-    {
+        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));
+      }
+      break;
+    case  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)
-    {
+      break;
+    case 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;
+      break;
+    case 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));
+      break;
+    default:
+      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 1068618)
+++ subversion/libsvn_wc/wc-queries.sql (working copy)
@@ -736,7 +736,62 @@
 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, caching results
+                     in a temporary table. */
 
+-- STMT_CLEAR_NODE_PROPS_CACHE
+DROP TABLE IF EXISTS temp__node_props_cache;
+
+-- STMT_CACHE_NODE_PROPS_RECURSIVE
+CREATE TEMPORARY TABLE temp__node_props_cache AS
+  SELECT local_relpath, kind, properties FROM nodes_current
+  WHERE wc_id = ?1
+    AND (?2 = '' OR local_relpath = ?2 OR local_relpath LIKE ?2 || '/%')
+    AND local_relpath NOT IN (
+      SELECT local_relpath FROM actual_node WHERE wc_id = ?1)
+    AND (presence = 'normal' OR presence = 'incomplete');
+CREATE UNIQUE INDEX temp__node_props_cache_unique
+  ON temp__node_props_cache (local_relpath);
+
+-- STMT_CACHE_ACTUAL_PROPS_RECURSIVE
+INSERT INTO temp__node_props_cache (local_relpath, kind, properties)
+  SELECT A.local_relpath, N.kind, A.properties
+  FROM actual_node AS A JOIN nodes_current AS N
+    ON A.wc_id = N.wc_id AND A.local_relpath = N.local_relpath
+       AND (N.presence = 'normal' OR N.presence = 'incomplete')
+  WHERE A.wc_id = ?1
+    AND (?2 = '' OR A.local_relpath = ?2 OR A.local_relpath LIKE ?2 || '/%')
+    AND A.local_relpath NOT IN
+      (SELECT local_relpath FROM temp__node_props_cache);
+
+-- STMT_CACHE_NODE_PROPS_OF_CHILDREN
+CREATE TEMPORARY TABLE temp__node_props_cache AS
+  SELECT local_relpath, kind, properties FROM nodes_current
+  WHERE wc_id = ?1
+    AND (local_relpath = ?2 OR parent_relpath = ?2)
+    AND local_relpath NOT IN (
+      SELECT local_relpath FROM actual_node WHERE wc_id = ?1)
+    AND (presence = 'normal' OR presence = 'incomplete');
+CREATE UNIQUE INDEX temp__node_props_cache_unique
+  ON temp__node_props_cache (local_relpath);
+
+-- STMT_CACHE_ACTUAL_PROPS_OF_CHILDREN
+INSERT INTO temp__node_props_cache (local_relpath, kind, properties)
+  SELECT A.local_relpath, N.kind, A.properties
+  FROM actual_node AS A JOIN nodes_current AS N
+    ON A.wc_id = N.wc_id AND A.local_relpath = N.local_relpath
+       AND (N.presence = 'normal' OR N.presence = 'incomplete')
+  WHERE A.wc_id = ?1
+    AND (A.local_relpath = ?2 OR A.parent_relpath = ?2)
+    AND A.local_relpath NOT IN
+      (SELECT local_relpath FROM temp__node_props_cache);
+
+-- STMT_SELECT_RELEVANT_PROPS_FROM_CACHE
+SELECT local_relpath, kind, properties FROM temp__node_props_cache
+ORDER BY local_relpath;
+
+
 /* ------------------------------------------------------------------------- */
 
 /* Grab all the statements related to the schema.  */
Index: subversion/libsvn_wc/wc-metadata.sql
===================================================================
--- subversion/libsvn_wc/wc-metadata.sql        (revision 1068618)
+++ subversion/libsvn_wc/wc-metadata.sql        (working copy)
@@ -483,6 +483,16 @@
 
 CREATE INDEX I_NODES_PARENT ON NODES (wc_id, parent_relpath, op_depth);
 
+/* Many queries have to filter the nodes table to pick only that version
+   of each node with the highest (most "current") op_depth.  This view
+   does the heavy lifting for such queries. */
+CREATE VIEW NODES_CURRENT AS
+  SELECT * FROM nodes
+    JOIN (SELECT wc_id, local_relpath, MAX(op_depth) AS op_depth FROM nodes
+          GROUP BY wc_id, local_relpath) AS filter
+    ON nodes.wc_id = filter.wc_id
+      AND nodes.local_relpath = filter.local_relpath
+      AND nodes.op_depth = filter.op_depth;
 
 -- STMT_CREATE_NODES_TRIGGERS
 
@@ -595,7 +605,22 @@
 
 PRAGMA user_version = 24;
 
+/* ------------------------------------------------------------------------- */
 
+/* Format 25 introduces the NODES_CURRENT view. */
+
+-- STMT_UPGRADE_TO_25
+DROP VIEW IF EXISTS NODES_CURRENT;
+CREATE VIEW NODES_CURRENT AS
+  SELECT * FROM nodes
+    JOIN (SELECT wc_id, local_relpath, MAX(op_depth) AS op_depth FROM nodes
+          GROUP BY wc_id, local_relpath) AS filter
+    ON nodes.wc_id = filter.wc_id
+      AND nodes.local_relpath = filter.local_relpath
+      AND nodes.op_depth = filter.op_depth;
+
+PRAGMA user_version = 25;
+
 /* ------------------------------------------------------------------------- */
 
 /* Format YYY introduces new handling for conflict information.  */
Index: subversion/libsvn_wc/wc_db.c
===================================================================
--- subversion/libsvn_wc/wc_db.c        (revision 1068618)
+++ subversion/libsvn_wc/wc_db.c        (working copy)
@@ -5169,188 +5169,152 @@
   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. */
+/* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
+ * a hash table mapping <tt>char *</tt> names onto svn_string_t *
+ * 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.
+ */
+typedef struct cache_props_baton_t
+{
+  int stmt_node_props_ndx;
+  int stmt_actual_props_ndx;
+  apr_int64_t wc_id;
+  const char *local_relpath;
+  svn_cancel_func_t cancel_func;
+  void *cancel_baton;
+} cache_props_baton_t;
+
 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,
+cache_props_recursive(void *cb_baton,
+                      svn_sqlite__db_t *db,
                       apr_pool_t *scratch_pool)
 {
-  const char *child_abspath;
-  apr_hash_t *props;
-  svn_skel_t *prop_skel;
+  cache_props_baton_t *baton = cb_baton;
+  svn_sqlite__stmt_t *stmt;
 
-  prop_skel = svn_skel__parse(prop_data, prop_data_len, scratch_pool);
-  if (svn_skel__list_length(prop_skel) == 0)
-    return SVN_NO_ERROR;
+  SVN_ERR(svn_sqlite__get_statement(&stmt, db, baton->stmt_node_props_ndx));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", baton->wc_id, baton->local_relpath));
+  SVN_ERR(svn_sqlite__step_done(stmt));
 
-  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);
+  if (baton->cancel_func)
+    SVN_ERR(baton->cancel_func(baton->cancel_baton));
 
+  SVN_ERR(svn_sqlite__get_statement(&stmt, db, baton->stmt_actual_props_ndx));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", baton->wc_id, baton->local_relpath));
+  SVN_ERR(svn_sqlite__step_done(stmt));
   return SVN_NO_ERROR;
 }
 
-/* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
- * a hash table mapping <tt>char *</tt> names onto svn_string_t *
- * values for any properties of immediate child nodes of LOCAL_ABSPATH.
- * If FILES_ONLY is true, only report properties for file child nodes.
- */
 static svn_error_t *
-read_props_of_children(svn_wc__db_t *db,
-                       const char *local_abspath,
-                       svn_boolean_t files_only,
-                       svn_wc__proplist_receiver_t receiver_func,
-                       void *receiver_baton,
-                       apr_pool_t *scratch_pool)
+read_props_recursive(svn_wc__db_t *db,
+                     const char *local_abspath,
+                     svn_boolean_t files_only,
+                     svn_boolean_t immediates_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;
   svn_sqlite__stmt_t *stmt;
+  cache_props_baton_t baton;
   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;
+  int row_number;
   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,
+  SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &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);
+  SVN_ERR(svn_sqlite__exec_statements(pdh->wcroot->sdb,
+                                      STMT_CLEAR_NODE_PROPS_CACHE));
+
+  if (immediates_only)
+    {
+      baton.stmt_node_props_ndx = STMT_CACHE_NODE_PROPS_OF_CHILDREN;
+      baton.stmt_actual_props_ndx = STMT_CACHE_ACTUAL_PROPS_OF_CHILDREN;
+    }
   else
-    files = NULL;
+    {
+      baton.stmt_node_props_ndx = STMT_CACHE_NODE_PROPS_RECURSIVE;
+      baton.stmt_actual_props_ndx = STMT_CACHE_ACTUAL_PROPS_RECURSIVE;
+    }
+  baton.wc_id = pdh->wcroot->wc_id;
+  baton.cancel_func = cancel_func;
+  baton.cancel_baton = cancel_baton;
+  SVN_ERR(svn_sqlite__with_transaction(pdh->wcroot->sdb,
+                                       cache_props_recursive,
+                                       &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)
+  for (row_number = 0; have_row; ++row_number)
     {
-      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 && row_number > 0)
         {
-          /* 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, 1, 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, 2, &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_pool_destroy(iterpool);
+  SVN_ERR(svn_sqlite__exec_statements(pdh->wcroot->sdb,
+                                      STMT_CLEAR_NODE_PROPS_CACHE));
 
   return SVN_NO_ERROR;
 }
@@ -5360,11 +5324,15 @@
                                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_recursive(db, local_abspath, TRUE, TRUE,
+                               receiver_func, receiver_baton,
+                               cancel_func, cancel_baton,
+                               scratch_pool));
+  return SVN_NO_ERROR;
 }
 
 svn_error_t *
@@ -5372,13 +5340,32 @@
                                     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_recursive(db, local_abspath, FALSE, TRUE,
+                               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_recursive(db, local_abspath, FALSE, 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 1068618)
+++ 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 <tt>char *</tt> 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).
 
Index: subversion/libsvn_wc/upgrade.c
===================================================================
--- subversion/libsvn_wc/upgrade.c      (revision 1068618)
+++ subversion/libsvn_wc/upgrade.c      (working copy)
@@ -1137,7 +1137,14 @@
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+bump_to_25(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
+{
+  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_25));
+  return SVN_NO_ERROR;
+}
 
+
 struct upgrade_data_t {
   svn_sqlite__db_t *sdb;
   const char *root_abspath;
@@ -1404,6 +1411,12 @@
         *result_format = 24;
         /* FALLTHROUGH  */
 
+      case 24:
+        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_25, &bb,
+                                             scratch_pool));
+        *result_format = 25;
+        /* FALLTHROUGH  */
+
       /* ### future bumps go here.  */
 #if 0
       case XXX-1:

Reply via email to