You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by st...@apache.org on 2010/12/29 22:12:38 UTC

svn commit: r1053735 [4/8] - in /subversion/branches/performance: ./ build/ build/ac-macros/ build/generator/ build/generator/swig/ contrib/client-side/svn_load_dirs/ notes/ subversion/bindings/javahl/native/ subversion/bindings/swig/ subversion/includ...

Modified: subversion/branches/performance/subversion/libsvn_wc/upgrade.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_wc/upgrade.c?rev=1053735&r1=1053734&r2=1053735&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_wc/upgrade.c (original)
+++ subversion/branches/performance/subversion/libsvn_wc/upgrade.c Wed Dec 29 21:12:33 2010
@@ -622,15 +622,6 @@ ensure_repos_info(svn_wc_entry_t *entry,
 }
 
 
-/* */
-static svn_error_t *
-bump_to_13(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
-{
-  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_13));
-
-  return SVN_NO_ERROR;
-}
-
 /*
  * Read tree conflict descriptions from @a conflict_data.  Set @a *conflicts
  * to a hash of pointers to svn_wc_conflict_description2_t objects indexed by
@@ -804,68 +795,10 @@ migrate_tree_conflict_data(svn_sqlite__d
 }
 
 
-/* */
-static svn_error_t *
-migrate_locks(const char *wcroot_abspath,
-              svn_sqlite__db_t *sdb,
-              apr_pool_t *scratch_pool)
-{
-  const char *lockfile_abspath = build_lockfile_path(wcroot_abspath,
-                                                     scratch_pool);
-  svn_node_kind_t kind;
-
-  SVN_ERR(svn_io_check_path(lockfile_abspath, &kind, scratch_pool));
-  if (kind != svn_node_none)
-    {
-      svn_sqlite__stmt_t *stmt;
-      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_WC_LOCK));
-      /* ### These values are magic, and will need to be updated when we
-         ### go to a centralized system. */
-      SVN_ERR(svn_sqlite__bindf(stmt, "is", (apr_int64_t)1, ""));
-      SVN_ERR(svn_sqlite__step_done(stmt));
-    }
-
-  return SVN_NO_ERROR;
-}
-
 struct bump_baton {
   const char *wcroot_abspath;
 };
 
-/* */
-static svn_error_t *
-bump_to_14(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
-{
-  const char *wcroot_abspath = ((struct bump_baton *)baton)->wcroot_abspath;
-
-  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_14));
-
-  SVN_ERR(migrate_locks(wcroot_abspath, sdb, scratch_pool));
-
-  return SVN_NO_ERROR;
-}
-
-
-/* */
-static svn_error_t *
-bump_to_15(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
-{
-  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_15));
-
-  return SVN_NO_ERROR;
-}
-
-
-/* */
-static svn_error_t *
-bump_to_16(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
-{
-  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_16));
-
-  return SVN_NO_ERROR;
-}
-
-
 /* Migrate the properties for one node (LOCAL_ABSPATH).  */
 static svn_error_t *
 migrate_node_props(const char *dir_abspath,
@@ -1004,33 +937,36 @@ migrate_props(const char *dir_abspath,
 }
 
 
-/* */
-struct bump_to_18_baton
-{
-  const char *wcroot_abspath;
-  int original_format;
-};
-
-
-static svn_error_t *
-bump_to_18(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
+/* If STR ends with SUFFIX and is longer than SUFFIX, return the part of
+ * STR that comes before SUFFIX; else return NULL. */
+static char *
+remove_suffix(const char *str, const char *suffix, apr_pool_t *result_pool)
 {
-  struct bump_to_18_baton *b18 = baton;
-
-  /* ### no schema changes (yet)... */
-  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_18));
+  int str_len = strlen(str);
+  int suffix_len = strlen(suffix);
 
-  SVN_ERR(migrate_props(b18->wcroot_abspath, b18->wcroot_abspath, sdb,
-                        b18->original_format, scratch_pool));
+  if (str_len > suffix_len
+      && strcmp(str + str_len - suffix_len, suffix) == 0)
+    {
+      return apr_pstrmemdup(result_pool, str, str_len - suffix_len);
+    }
 
-  return SVN_NO_ERROR;
+  return NULL;
 }
 
+/* Copy all the text-base files from the administrative area of WC directory
+   DIR_ABSPATH into the pristine store of SDB which is located in directory
+   NEW_WCROOT_ABSPATH.
 
+   Set *TEXT_BASES_INFO to a new hash, allocated in RESULT_POOL, that maps
+   (const char *) name of the versioned file to (svn_wc__text_base_info_t *)
+   information about the pristine text. */
 static svn_error_t *
-migrate_text_bases(const char *dir_abspath,
+migrate_text_bases(apr_hash_t **text_bases_info,
+                   const char *dir_abspath,
                    const char *new_wcroot_abspath,
                    svn_sqlite__db_t *sdb,
+                   apr_pool_t *result_pool,
                    apr_pool_t *scratch_pool)
 {
   apr_hash_t *dirents;
@@ -1040,80 +976,111 @@ migrate_text_bases(const char *dir_abspa
                                                 TEXT_BASE_SUBDIR,
                                                 scratch_pool);
 
+  *text_bases_info = apr_hash_make(result_pool);
+
+  /* Iterate over the text-base files */
   SVN_ERR(svn_io_get_dirents3(&dirents, text_base_dir, TRUE,
                               scratch_pool, scratch_pool));
   for (hi = apr_hash_first(scratch_pool, dirents); hi;
-            hi = apr_hash_next(hi))
+       hi = apr_hash_next(hi))
     {
       const char *text_base_basename = svn__apr_hash_index_key(hi);
-      const char *pristine_path;
-      const char *text_base_path;
       svn_checksum_t *md5_checksum;
       svn_checksum_t *sha1_checksum;
-      svn_sqlite__stmt_t *stmt;
-      apr_finfo_t finfo;
 
       svn_pool_clear(iterpool);
-      text_base_path = svn_dirent_join(text_base_dir, text_base_basename,
-                                       iterpool);
-
-      /* ### This code could be a bit smarter: we could chain checksum
-             streams instead of reading the file twice; we could check to
-             see if a pristine row exists before attempting to insert one;
-             we could check and see if a pristine file exists before
-             attempting to copy a new one over it.
-             
-             However, I think simplicity is the big win here, especially since
-             this is code that runs exactly once on a user's machine: when
-             doing the upgrade.  If you disagree, feel free to add the
-             complexity. :)  */
-
-      /* Gather the two checksums. */
-      SVN_ERR(svn_io_file_checksum2(&md5_checksum, text_base_path,
-                                    svn_checksum_md5, iterpool));
-      SVN_ERR(svn_io_file_checksum2(&sha1_checksum, text_base_path,
-                                    svn_checksum_sha1, iterpool));
-
-      SVN_ERR(svn_io_stat(&finfo, text_base_path, APR_FINFO_SIZE, iterpool));
-
-      /* Insert a row into the pristine table. */
-      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_PRISTINE));
-      SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, iterpool));
-      SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, iterpool));
-      SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size));
-      SVN_ERR(svn_sqlite__insert(NULL, stmt));
-
-      SVN_ERR(svn_wc__db_pristine_get_future_path(&pristine_path,
-                                                  new_wcroot_abspath,
-                                                  sha1_checksum,
-                                                  iterpool, iterpool));
-
-      /* Ensure any sharding directories exist. */
-      SVN_ERR(svn_wc__ensure_directory(svn_dirent_dirname(pristine_path,
-                                                          iterpool),
-                                       iterpool));
-
-      /* Copy, rather than move, so that the upgrade can be restarted.
-         It could be moved if upgrades scanned for files in the
-         pristine directory as well as the text-base directory. */
-      SVN_ERR(svn_io_copy_file(text_base_path, pristine_path, TRUE,
-                               iterpool));
-    }
 
-  svn_pool_destroy(iterpool);
-
-  return SVN_NO_ERROR;
-}
+      /* Calculate its checksums and copy it to the pristine store */
+      {
+        const char *pristine_path;
+        const char *text_base_path;
+        svn_sqlite__stmt_t *stmt;
+        apr_finfo_t finfo;
 
+        text_base_path = svn_dirent_join(text_base_dir, text_base_basename,
+                                         iterpool);
 
-static svn_error_t *
-bump_to_17(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
-{
-  const char *wcroot_abspath = ((struct bump_baton *)baton)->wcroot_abspath;
+        /* ### This code could be a bit smarter: we could chain checksum
+               streams instead of reading the file twice; we could check to
+               see if a pristine row exists before attempting to insert one;
+               we could check and see if a pristine file exists before
+               attempting to copy a new one over it.
+
+               However, I think simplicity is the big win here, especially since
+               this is code that runs exactly once on a user's machine: when
+               doing the upgrade.  If you disagree, feel free to add the
+               complexity. :)  */
+
+        /* Gather the two checksums. */
+        SVN_ERR(svn_io_file_checksum2(&md5_checksum, text_base_path,
+                                      svn_checksum_md5, iterpool));
+        SVN_ERR(svn_io_file_checksum2(&sha1_checksum, text_base_path,
+                                      svn_checksum_sha1, iterpool));
+
+        SVN_ERR(svn_io_stat(&finfo, text_base_path, APR_FINFO_SIZE, iterpool));
+
+        /* Insert a row into the pristine table. */
+        SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_PRISTINE));
+        SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, iterpool));
+        SVN_ERR(svn_sqlite__bind_checksum(stmt, 2, md5_checksum, iterpool));
+        SVN_ERR(svn_sqlite__bind_int64(stmt, 3, finfo.size));
+        SVN_ERR(svn_sqlite__insert(NULL, stmt));
+
+        SVN_ERR(svn_wc__db_pristine_get_future_path(&pristine_path,
+                                                    new_wcroot_abspath,
+                                                    sha1_checksum,
+                                                    iterpool, iterpool));
+
+        /* Ensure any sharding directories exist. */
+        SVN_ERR(svn_wc__ensure_directory(svn_dirent_dirname(pristine_path,
+                                                            iterpool),
+                                         iterpool));
+
+        /* Copy, rather than move, so that the upgrade can be restarted.
+           It could be moved if upgrades scanned for files in the
+           pristine directory as well as the text-base directory. */
+        SVN_ERR(svn_io_copy_file(text_base_path, pristine_path, TRUE,
+                                 iterpool));
+      }
+
+      /* Add the checksums for this text-base to *TEXT_BASES_INFO. */
+      {
+        const char *versioned_file_name;
+        svn_boolean_t is_revert_base;
+        svn_wc__text_base_info_t *info;
+        svn_wc__text_base_file_info_t *file_info;
+
+        /* Determine the versioned file name and whether this is a normal base
+         * or a revert base. */
+        versioned_file_name = remove_suffix(text_base_basename,
+                                            SVN_WC__REVERT_EXT, result_pool);
+        if (versioned_file_name)
+          {
+            is_revert_base = TRUE;
+          }
+        else
+          {
+            versioned_file_name = remove_suffix(text_base_basename,
+                                                SVN_WC__BASE_EXT, result_pool);
+            is_revert_base = FALSE;
+          }
+
+        /* Create a new info struct for this versioned file, or fill in the
+         * existing one if this is the second text-base we've found for it. */
+        info = apr_hash_get(*text_bases_info, versioned_file_name,
+                            APR_HASH_KEY_STRING);
+        if (info == NULL)
+          info = apr_pcalloc(result_pool, sizeof (*info));
+        file_info = (is_revert_base ? &info->revert_base : &info->normal_base);
+
+        file_info->sha1_checksum = svn_checksum_dup(sha1_checksum, result_pool);
+        file_info->md5_checksum = svn_checksum_dup(md5_checksum, result_pool);
+        apr_hash_set(*text_bases_info, versioned_file_name, APR_HASH_KEY_STRING,
+                     info);
+      }
+    }
 
-  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_17));
-  SVN_ERR(migrate_text_bases(wcroot_abspath, wcroot_abspath, sdb,
-                             scratch_pool));
+  svn_pool_destroy(iterpool);
 
   return SVN_NO_ERROR;
 }
@@ -1141,6 +1108,27 @@ bump_to_22(void *baton, svn_sqlite__db_t
   return SVN_NO_ERROR;
 }
 
+static svn_error_t *
+bump_to_23(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
+{
+  const char *wcroot_abspath = ((struct bump_baton *)baton)->wcroot_abspath;
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_HAS_WORKING_NODES));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+  SVN_ERR(svn_sqlite__reset(stmt));
+  if (have_row)
+    return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
+                             _("The working copy at '%s' is format 22 with "
+                               "WORKING nodes; use a format 22 client to "
+                               "diff/revert before using this client"),
+                             wcroot_abspath);
+
+  SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_UPGRADE_TO_23));
+  return SVN_NO_ERROR;
+}
+
 
 struct upgrade_data_t {
   svn_sqlite__db_t *sdb;
@@ -1180,6 +1168,8 @@ upgrade_to_wcng(void **dir_baton,
   svn_node_kind_t logfile_on_disk;
   apr_hash_t *entries;
   svn_wc_entry_t *this_dir;
+  const char *old_wcroot_abspath, *dir_relpath;
+  apr_hash_t *text_bases_info;
 
   /* Don't try to mess with the WC if there are old log files left. */
 
@@ -1201,18 +1191,21 @@ upgrade_to_wcng(void **dir_baton,
    * The semantics and storage mechanisms between the two are vastly different,
    * so it's going to be a bit painful.  Here's a plan for the operation:
    *
-   * 1) The 'entries' file needs to be moved to the new format. We read it
-   *    using the old-format reader, and then use our compatibility code
-   *    for writing entries to fill out the (new) wc_db state.
+   * 1) Read the old 'entries' using the old-format reader.
+   *
+   * 2) Create the new DB if it hasn't already been created.
    *
-   * 2) Convert wcprop to the wc-ng format
+   * 3) Use our compatibility code for writing entries to fill out the (new)
+   *    DB state.  Use the remembered checksums, since an entry has only the
+   *    MD5 not the SHA1 checksum, and in the case of a revert-base doesn't
+   *    even have that.
    *
-   * 3) Trash old, unused files and subdirs
+   * 4) Convert wcprop to the wc-ng format
    *
-   * ### (fill in other bits as they are implemented)
+   * 5) Migrate regular properties to the WC-NG DB.
    */
 
-  /***** ENTRIES *****/
+  /***** ENTRIES - READ *****/
   SVN_ERR(svn_wc__read_entries_old(&entries, dir_abspath,
                                    scratch_pool, scratch_pool));
 
@@ -1233,6 +1226,7 @@ upgrade_to_wcng(void **dir_baton,
                    apr_pstrdup(hash_pool, this_dir->uuid));
     }
 
+  /* Create the new DB if it hasn't already been created. */
   if (!data->sdb)
     {
       const char *root_adm_abspath;
@@ -1266,24 +1260,28 @@ upgrade_to_wcng(void **dir_baton,
       SVN_ERR(svn_wc__db_wclock_obtain(db, data->root_abspath, 0, FALSE,
                                        scratch_pool));
     }
- 
+
+  old_wcroot_abspath = svn_dirent_get_longest_ancestor(dir_abspath,
+                                                       data->root_abspath,
+                                                       scratch_pool);
+  dir_relpath = svn_dirent_skip_ancestor(old_wcroot_abspath, dir_abspath);
+
+  /***** TEXT BASES *****/
+  SVN_ERR(migrate_text_bases(&text_bases_info, dir_abspath, data->root_abspath,
+                             data->sdb, scratch_pool, scratch_pool));
+
+  /***** ENTRIES - WRITE *****/
   SVN_ERR(svn_wc__write_upgraded_entries(dir_baton, parent_baton, db, data->sdb,
                                          data->repos_id, data->wc_id,
                                          dir_abspath, data->root_abspath,
-                                         entries,
+                                         entries, text_bases_info,
                                          result_pool, scratch_pool));
 
   /***** WC PROPS *****/
-
-  /* Ugh. We don't know precisely where the wcprops are. Ignore them.  */
+  /* If we don't know precisely where the wcprops are, ignore them.  */
   if (old_format != SVN_WC__WCPROPS_LOST)
     {
       apr_hash_t *all_wcprops;
-      const char *old_wcroot_abspath
-        = svn_dirent_get_longest_ancestor(dir_abspath, data->root_abspath,
-                                          scratch_pool);
-      const char *dir_relpath = svn_dirent_skip_ancestor(old_wcroot_abspath,
-                                                         dir_abspath);
 
       if (old_format <= SVN_WC__WCPROPS_MANY_FILES_VERSION)
         SVN_ERR(read_many_wcprops(&all_wcprops, dir_abspath,
@@ -1296,9 +1294,6 @@ upgrade_to_wcng(void **dir_baton,
                                                  all_wcprops, scratch_pool));
     }
 
-  SVN_ERR(migrate_text_bases(dir_abspath, data->root_abspath, data->sdb,
-                             scratch_pool));
-
   /* Upgrade all the properties (including "this dir").
 
      Note: this must come AFTER the entries have been migrated into the
@@ -1314,6 +1309,25 @@ upgrade_to_wcng(void **dir_baton,
 }
 
 
+/* Return a string indicating the released version (or versions) of
+ * Subversion that used WC format number WC_FORMAT, or some other
+ * suitable string if no released version used WC_FORMAT.
+ *
+ * ### It's not ideal to encode this sort of knowledge in this low-level
+ * library.  On the other hand, it doesn't need to be updated often and
+ * should be easily found when it does need to be updated.  */
+static const char *
+version_string_from_format(int wc_format)
+{
+  switch (wc_format)
+    {
+      case 4: return "<=1.3";
+      case 8: return "1.4";
+      case 9: return "1.5";
+      case 10: return "1.6";
+    }
+  return _("(unreleased development version)");
+}
 
 svn_error_t *
 svn_wc__upgrade_sdb(int *result_format,
@@ -1326,8 +1340,21 @@ svn_wc__upgrade_sdb(int *result_format,
 
   if (start_format < SVN_WC__WC_NG_VERSION /* 12 */)
     return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, NULL,
-                             _("Working copy format of '%s' is too old (%d); "
-                               "please run 'svn upgrade'"),
+                             _("Working copy '%s' is too old (format %d, "
+                               "created by Subversion %s)"),
+                             svn_dirent_local_style(wcroot_abspath,
+                                                    scratch_pool),
+                             start_format,
+                             version_string_from_format(start_format));
+
+  /* Early WCNG formats no longer supported. */
+  if (start_format < 19)
+    return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, NULL,
+                             _("Working copy '%s' is an old development "
+                               "version (format %d); to upgrade it, "
+                               "use a format 18 client, then "
+                               "use 'tools/dev/wc-ng/bump-to-19.py', then "
+                               "use the current client"),
                              svn_dirent_local_style(wcroot_abspath,
                                                     scratch_pool),
                              start_format);
@@ -1339,86 +1366,6 @@ svn_wc__upgrade_sdb(int *result_format,
      intentional. */
   switch (start_format)
     {
-      case 12:
-        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_13, &bb,
-                                             scratch_pool));
-        /* If the transaction succeeded, then we don't need the wcprops
-           files. We stopped writing them partway through format 12, but
-           we may be upgrading from an "early 12" and need to toss those
-           files. We aren't going to migrate them because it is *also*
-           possible that current/real data is sitting within the database.
-           This is why STMT_UPGRADE_TO_13 just clears the 'dav_cache'
-           column -- we cannot definitely state that the column values
-           are Proper.
-
-           They're removed by wipe_obsolete_files(), below.  */
-
-        *result_format = 13;
-        /* FALLTHROUGH  */
-
-      case 13:
-        /* Build WCLOCKS and migrate any physical lock.  */
-        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_14, &bb,
-                                             scratch_pool));
-        /* If the transaction succeeded, then any lock has been migrated,
-           and we can toss the physical file (below).  */
-
-        *result_format = 14;
-        /* FALLTHROUGH  */
-
-      case 14:
-        /* Revamp the recording of 'excluded' nodes.  */
-        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_15, &bb,
-                                             scratch_pool));
-        *result_format = 15;
-        /* FALLTHROUGH  */
-
-      case 15:
-        /* Perform some minor changes to the schema.  */
-        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_16, &bb,
-                                             scratch_pool));
-        *result_format = 16;
-        /* FALLTHROUGH  */
-
-      case 16:
-        {
-          const char *pristine_dir;
-
-          /* Create the '.svn/pristine' directory.  */
-          pristine_dir = svn_wc__adm_child(wcroot_abspath,
-                                           SVN_WC__ADM_PRISTINE,
-                                           scratch_pool);
-          SVN_ERR(svn_wc__ensure_directory(pristine_dir, scratch_pool));
-
-          /* Move text bases into the pristine directory, and update the db */
-          SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_17, &bb,
-                                               scratch_pool));
-        }
-
-        *result_format = 17;
-        /* FALLTHROUGH  */
-
-      case 17:
-        {
-          struct bump_to_18_baton b18;
-
-          b18.wcroot_abspath = wcroot_abspath;
-          b18.original_format = start_format;
-
-          /* Move the properties into the database.  */
-          SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_18, &b18,
-                                               scratch_pool));
-        }
-
-        *result_format = 18;
-        /* FALLTHROUGH  */
-
-      case 18:
-        return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
-                                 _("The working copy '%s' is at format 18; "
-                                   "use 'tools/dev/wc-ng/bump-to-19.py' to "
-                                   "upgrade it"), wcroot_abspath);
-
       case 19:
         SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_20, &bb,
                                              scratch_pool));
@@ -1437,6 +1384,12 @@ svn_wc__upgrade_sdb(int *result_format,
         *result_format = 22;
         /* FALLTHROUGH  */
 
+      case 22:
+        SVN_ERR(svn_sqlite__with_transaction(sdb, bump_to_23, &bb,
+                                             scratch_pool));
+        *result_format = 23;
+        /* FALLTHROUGH  */
+
       /* ### future bumps go here.  */
 #if 0
       case XXX-1:

Modified: subversion/branches/performance/subversion/libsvn_wc/wc-metadata.sql
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_wc/wc-metadata.sql?rev=1053735&r1=1053734&r2=1053735&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_wc/wc-metadata.sql (original)
+++ subversion/branches/performance/subversion/libsvn_wc/wc-metadata.sql Wed Dec 29 21:12:33 2010
@@ -167,9 +167,9 @@ CREATE TABLE ACTUAL_NODE (
            conflicts? Why do we need these in a column to refer to the
            pristine store? Can't we just parse the checksums from
            conflict_data as well? */
-  older_checksum  TEXT,
-  left_checksum  TEXT,
-  right_checksum  TEXT,
+  older_checksum  TEXT REFERENCES PRISTINE (checksum),
+  left_checksum  TEXT REFERENCES PRISTINE (checksum),
+  right_checksum  TEXT REFERENCES PRISTINE (checksum),
 
   PRIMARY KEY (wc_id, local_relpath)
   );
@@ -327,8 +327,6 @@ CREATE TABLE NODES (
 
   /* The tree state of the node.
 
-     ### This applies to SVN_WC__OP_DEPTH, the intended final code!
-
      In case 'op_depth' is equal to 0, this node is part of the 'BASE'
      tree.  The 'BASE' represents pristine nodes that are in the
      repository; it is obtained and modified by commands such as
@@ -417,7 +415,7 @@ CREATE TABLE NODES (
 
   /* The SHA-1 checksum of the pristine text, if this node is a file and was
      moved here or copied here, else NULL. */
-  checksum  TEXT,
+  checksum  TEXT REFERENCES PRISTINE (checksum),
 
   /* for kind==symlink, this specifies the target. */
   symlink_target  TEXT,
@@ -483,126 +481,6 @@ CREATE INDEX I_NODES_PARENT ON NODES (wc
 
 
 
-/* ------------------------------------------------------------------------- */
-
-/* Format 13 introduces the work queue, and erases a few columns from the
-   original schema.  */
--- STMT_UPGRADE_TO_13
-
-CREATE TABLE WORK_QUEUE (
-  /* Work items are identified by this value.  */
-  id  INTEGER PRIMARY KEY AUTOINCREMENT,
-
-  /* A serialized skel specifying the work item.  */
-  work  BLOB NOT NULL
-  );
-
-/* The contents of dav_cache are suspect in format 12, so it is best to just
-   erase anything there.  */
-UPDATE BASE_NODE SET incomplete_children=null, dav_cache=null;
-
-PRAGMA user_version = 13;
-
-
-/* ------------------------------------------------------------------------- */
-
-/* Format 14 introduces a table for storing wc locks, and additional columns
-   for storing conflict data in ACTUAL. */
--- STMT_UPGRADE_TO_14
-
-/* The existence of a row in this table implies a write lock. */
-CREATE TABLE WC_LOCK (
-  /* specifies the location of this node in the local filesystem */
-  wc_id  INTEGER NOT NULL  REFERENCES WCROOT (id),
-  local_dir_relpath  TEXT NOT NULL,
- 
-  PRIMARY KEY (wc_id, local_dir_relpath)
- );
-
-/* A skel containing the conflict details. */
-ALTER TABLE ACTUAL_NODE
-ADD COLUMN conflict_data  BLOB;
-
-/* Three columns containing the checksums of older, left and right conflict
-   texts.  Stored in a column to allow storing them in the pristine store */
-ALTER TABLE ACTUAL_NODE
-ADD COLUMN older_checksum  TEXT;
-
-ALTER TABLE ACTUAL_NODE
-ADD COLUMN left_checksum  TEXT;
-
-ALTER TABLE ACTUAL_NODE
-ADD COLUMN right_checksum  TEXT;
-
-PRAGMA user_version = 14;
-
-
-/* ------------------------------------------------------------------------- */
-
-/* Format 15 introduces new handling for excluded nodes.  */
--- STMT_UPGRADE_TO_15
-
-UPDATE base_node
-SET
-  presence = 'excluded',
-  checksum = NULL, translated_size = NULL, changed_rev = NULL,
-  changed_date = NULL, changed_author = NULL, depth = NULL,
-  symlink_target = NULL, last_mod_time = NULL, properties = NULL,
-  incomplete_children = NULL, file_external = NULL
-WHERE depth = 'exclude';
-
-/* We don't support cropping working nodes, but we might see them
-   via a copy from a sparse tree. Convert them anyway to make sure
-   we never see depth exclude in our database */
-UPDATE working_node
-SET
-  presence = 'excluded',
-  checksum = NULL, translated_size = NULL, changed_rev = NULL,
-  changed_date = NULL, changed_author = NULL, depth = NULL,
-  symlink_target = NULL, copyfrom_repos_id = NULL, copyfrom_repos_path = NULL,
-  copyfrom_revnum = NULL, moved_here = NULL, moved_to = NULL,
-  last_mod_time = NULL, properties = NULL, keep_local = NULL
-WHERE depth = 'exclude';
-
-PRAGMA user_version = 15;
-
-
-/* ------------------------------------------------------------------------- */
-
-/* Format 16 introduces some new columns for pristines and locks.  */
--- STMT_UPGRADE_TO_16
-
-/* An md5 column for the pristine table. */
-ALTER TABLE PRISTINE
-ADD COLUMN md5_checksum  TEXT;
-
-/* Add the locked_levels column to record the depth of a lock. */
-ALTER TABLE WC_LOCK
-ADD COLUMN locked_levels  INTEGER NOT NULL DEFAULT -1;
-
-/* Default the depth of existing locks to 0. */
-UPDATE wc_lock
-SET locked_levels = 0;
-
-PRAGMA user_version = 16;
-
-/* ------------------------------------------------------------------------- */
-
-/* Format 17 involves no schema changes, it moves the pristine files
-   from .svn/text-base to .svn/pristine */
-
--- STMT_UPGRADE_TO_17
-PRAGMA user_version = 17;
-
-/* ------------------------------------------------------------------------- */
-
-/* Format 18 involves no schema changes, it moves the property data from files
-   into the database. */
-
--- STMT_UPGRADE_TO_18
-PRAGMA user_version = 18;
-
-
 /* Format 20 introduces NODES and removes BASE_NODE and WORKING_NODE */
 
 -- STMT_UPGRADE_TO_20
@@ -665,6 +543,15 @@ PRAGMA user_version = 22;
 
 /* ------------------------------------------------------------------------- */
 
+/* Format 23 involves no schema changes, it introduces multi-layer
+   op-depth processing for NODES. */
+
+-- STMT_UPGRADE_TO_23
+PRAGMA user_version = 23;
+
+
+/* ------------------------------------------------------------------------- */
+
 /* Format YYY introduces new handling for conflict information.  */
 -- format: YYY
 
@@ -723,8 +610,15 @@ FROM ACTUAL_NODE_BACKUP;
 
 DROP TABLE ACTUAL_NODE_BACKUP;
 
-/* Note: One difference remains between the schemas of an upgraded and a
- * fresh WC.  While format 22 was current, "NOT NULL" was added to the
+/* Note: Other differences between the schemas of an upgraded and a
+ * fresh WC.
+ *
+ * While format 22 was current, "NOT NULL" was added to the
  * columns PRISTINE.size and PRISTINE.md5_checksum.  The format was not
- * bumped because it is a forward- and backward-compatible change. */
+ * bumped because it is a forward- and backward-compatible change.
+ *
+ * While format 23 was current, "REFERENCES PRISTINE" was added to the
+ * columns ACTUAL_NODE.older_checksum, ACTUAL_NODE.left_checksum,
+ * ACTUAL_NODE.right_checksum, NODES.checksum.
+ */
 

Modified: subversion/branches/performance/subversion/libsvn_wc/wc-queries.sql
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_wc/wc-queries.sql?rev=1053735&r1=1053734&r2=1053735&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/branches/performance/subversion/libsvn_wc/wc-queries.sql Wed Dec 29 21:12:33 2010
@@ -104,6 +104,15 @@ LEFT OUTER JOIN lock ON nodes.repos_id =
   AND nodes.repos_path = lock.repos_relpath
 WHERE wc_id = ?1 AND parent_relpath = ?2;
 
+-- STMT_SELECT_NODE_CHILDREN_WALKER_INFO
+/* ### See comment at STMT_SELECT_NODE_CHILDREN_INFO.
+   ### Should C code handle GROUP BY local_relpath ORDER BY op_depths DESC? */
+SELECT local_relpath, op_depth, presence, kind
+FROM nodes
+WHERE wc_id = ?1 AND parent_relpath = ?2
+GROUP BY local_relpath
+ORDER BY op_depth DESC;
+
 -- STMT_SELECT_ACTUAL_CHILDREN_INFO
 SELECT prop_reject, changelist, conflict_old, conflict_new,
 conflict_working, tree_conflict_data, properties, local_relpath,
@@ -224,15 +233,11 @@ WHERE repos_id = ?1 AND
   (repos_relpath = ?2 OR
    repos_relpath LIKE ?3 ESCAPE '#');
 
--- STMT_UPDATE_BASE_NODE_FILEINFO
-UPDATE nodes SET translated_size = ?3, last_mod_time = ?4
-WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0;
-
--- STMT_UPDATE_WORKING_NODE_FILEINFO
+-- STMT_UPDATE_NODE_FILEINFO
 UPDATE nodes SET translated_size = ?3, last_mod_time = ?4
 WHERE wc_id = ?1 AND local_relpath = ?2
   AND op_depth = (SELECT MAX(op_depth) FROM nodes
-                  WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > 0);
+                  WHERE wc_id = ?1 AND local_relpath = ?2);
 
 -- STMT_UPDATE_ACTUAL_TREE_CONFLICTS
 UPDATE actual_node SET tree_conflict_data = ?3
@@ -320,11 +325,6 @@ WHERE wc_id = ?1 AND local_relpath = ?2;
 DELETE FROM actual_node
 WHERE wc_id = ?1 AND local_relpath = ?2;
 
--- STMT_DELETE_ACTUAL_NODE_WITHOUT_CHANGELIST
-DELETE FROM actual_node
-WHERE wc_id = ?1 AND local_relpath = ?2
-      AND changelist IS NULL;
-
 -- STMT_DELETE_ACTUAL_NODE_WITHOUT_CONFLICT
 DELETE FROM actual_node
 WHERE wc_id = ?1 AND local_relpath = ?2
@@ -387,10 +387,6 @@ WHERE wc_id = ?1 AND local_relpath = ?2 
 UPDATE nodes SET presence = ?3
 WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0;
 
--- STMT_UPDATE_NODE_BASE_PRESENCE_KIND
-UPDATE nodes SET presence = ?3, kind = ?4
-WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = 0;
-
 -- STMT_UPDATE_NODE_WORKING_PRESENCE
 UPDATE nodes SET presence = ?3
 WHERE wc_id = ?1 AND local_relpath = ?2
@@ -598,75 +594,6 @@ SELECT 1 FROM nodes WHERE wc_id = ?1 AND
   AND op_depth = (SELECT MAX(op_depth) FROM nodes
                   WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth > 0);
 
-
--- STMT_COPY_NODES_ROW
-INSERT OR REPLACE INTO nodes (
-    wc_id, local_relpath, op_depth, parent_relpath,
-    repos_id, repos_path, revision, presence, /* moved_here, moved_to, */
-    kind, properties, depth, checksum, symlink_target,
-    changed_revision, changed_date, changed_author,
-    translated_size, last_mod_time /* dav_cache, file_external */ )
-SELECT wc_id,
-    ?4 /*dst_relpath*/,
-    ?6 /*dst_op_depth*/,
-    ?5 /*dst_parent_*/,
-    repos_id, repos_path, revision, presence, /* moved_here, moved_to, */
-    kind, properties, depth, checksum, symlink_target,
-    changed_revision, changed_date, changed_author,
-    translated_size, last_mod_time /* dav_cache, file_external */
-FROM nodes
-WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3;
-
--- STMT_COPY_NODES_AT_SAME_OP_DEPTH
-INSERT OR REPLACE INTO nodes (
-    wc_id, local_relpath, op_depth, parent_relpath,
-    repos_id, repos_path, revision, presence, /* moved_here, moved_to, */
-    kind, properties, depth, checksum, symlink_target,
-    changed_revision, changed_date, changed_author,
-    translated_size, last_mod_time /* dav_cache, file_external */ )
-SELECT wc_id,
-    ?4 /*dst_relpath*/ || SUBSTR(local_relpath, ?6 /*LEN(src_relpath)+1*/),
-    ?5 /*dst_depth*/,
-    ?4 /*dst_relpath*/ || SUBSTR(parent_relpath, ?6 /*LEN(src_relpath)+1*/),
-    repos_id, repos_path, revision, presence, /* moved_here, moved_to, */
-    kind, properties, depth, checksum, symlink_target,
-    changed_revision, changed_date, changed_author,
-    translated_size, last_mod_time /* dav_cache, file_external */
-FROM nodes
-WHERE wc_id = ?1 AND local_relpath LIKE ?2 ESCAPE '#' AND op_depth = ?3;
-
--- STMT_COPY_NODES_AT_GREATER_OP_DEPTH
-INSERT OR REPLACE INTO nodes (
-    wc_id, local_relpath, op_depth, parent_relpath,
-    repos_id, repos_path, revision, presence, /* moved_here, moved_to, */
-    kind, properties, depth, checksum, symlink_target,
-    changed_revision, changed_date, changed_author,
-    translated_size, last_mod_time /* dav_cache, file_external */ )
-SELECT wc_id,
-    ?4 /*dst_relpath*/ || SUBSTR(local_relpath, ?6 /*LEN(src_relpath)+1*/),
-    op_depth + ?5 /*dst_depth*/ - ?3 /*src_depth*/,
-    ?4 /*dst_relpath*/ || SUBSTR(parent_relpath, ?6 /*LEN(src_relpath)+1*/),
-    repos_id, repos_path, revision, presence, /* moved_here, moved_to, */
-    kind, properties, depth, checksum, symlink_target,
-    changed_revision, changed_date, changed_author,
-    translated_size, last_mod_time /* dav_cache, file_external */
-FROM nodes
-WHERE wc_id = ?1 AND local_relpath LIKE ?2 ESCAPE '#' AND op_depth > ?3;
-
--- STMT_COPY_ACTUAL_NODE_ROWS
-INSERT OR REPLACE INTO actual_node (
-     wc_id, local_relpath, parent_relpath, properties,
-     conflict_old, conflict_new, conflict_working,
-     prop_reject, changelist, text_mod, tree_conflict_data )
-SELECT wc_id,
-     ?4 /*dst_relpath*/ || SUBSTR(local_relpath,  ?6 /*LEN(src_relpath)+1*/),
-     ?5 /*dst_parent_*/ || SUBSTR(parent_relpath, ?7 /*LEN(src_parent_)+1*/),
-     properties,
-     conflict_old, conflict_new, conflict_working,
-     prop_reject, changelist, text_mod, tree_conflict_data
-FROM actual_node
-WHERE wc_id = ?1 AND (local_relpath = ?2 OR local_relpath LIKE ?3 ESCAPE '#');
-
 -- STMT_SELECT_ABSENT_NODES
 SELECT local_relpath FROM nodes
 WHERE wc_id = ?1 AND (local_relpath = ?2 OR local_relpath LIKE ?3 ESCAPE '#')
@@ -794,14 +721,22 @@ UPDATE actual_node SET tree_conflict_dat
 SELECT DISTINCT local_relpath FROM nodes
 WHERE kind = 'file' AND parent_relpath = ?1;
 
--- STMT_PLAN_PROP_UPGRADE
-SELECT 0, nodes_base.presence, nodes_base.wc_id FROM nodes nodes_base
-WHERE nodes_base.local_relpath = ?1 AND nodes_base.op_depth = 0
-UNION ALL
-SELECT 1, nodes_work.presence, nodes_work.wc_id FROM nodes nodes_work
-WHERE nodes_work.local_relpath = ?1
-  AND nodes_work.op_depth = (SELECT MAX(op_depth) FROM nodes
-                             WHERE local_relpath = ?1 AND op_depth > 0);
+-- STMT_SELECT_NODE_UPGRADE
+SELECT op_depth, presence, wc_id
+FROM nodes
+WHERE local_relpath = ?1
+ORDER BY op_depth DESC;
+
+-- STMT_UPDATE_NODE_PROPS
+UPDATE nodes SET properties = ?4
+WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3;
+
+-- STMT_HAS_WORKING_NODES
+SELECT 1 FROM nodes WHERE op_depth > 0;
+
+-- STMT_UPDATE_CHECKSUM
+UPDATE nodes SET checksum = ?4
+WHERE wc_id = ?1 AND local_relpath = ?2 AND op_depth = ?3;
 
 
 /* ------------------------------------------------------------------------- */

Modified: subversion/branches/performance/subversion/libsvn_wc/wc.h
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_wc/wc.h?rev=1053735&r1=1053734&r2=1053735&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_wc/wc.h (original)
+++ subversion/branches/performance/subversion/libsvn_wc/wc.h Wed Dec 29 21:12:33 2010
@@ -124,12 +124,22 @@ extern "C" {
  * The change from 19 to 20 introduces NODES and drops BASE_NODE and
  * WORKING_NODE, op_depth is always 0 or 2. 
  *
+ * The change from 20 to 21 moved tree conflict storage from the
+ * parent to the conflicted node.
+ *
+ * The change from 21 to 22 moved tree conflict storage from
+ * conflict_data column to the tree_conflict_data column.
+ *
+ * The change from 22 to 23 introduced multi-layer op_depth processing for
+ * NODES.
+ *
  * == 1.7.x shipped with format ???
  *
  * Please document any further format changes here.
  */
 
-#define SVN_WC__VERSION 22
+#define SVN_WC__VERSION 23
+
 
 /* Formats <= this have no concept of "revert text-base/props".  */
 #define SVN_WC__NO_REVERT_FILES 4

Modified: subversion/branches/performance/subversion/libsvn_wc/wc_db.c
URL: http://svn.apache.org/viewvc/subversion/branches/performance/subversion/libsvn_wc/wc_db.c?rev=1053735&r1=1053734&r2=1053735&view=diff
==============================================================================
--- subversion/branches/performance/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/branches/performance/subversion/libsvn_wc/wc_db.c Wed Dec 29 21:12:33 2010
@@ -277,13 +277,6 @@ read_info(svn_wc__db_status_t *status,
           apr_pool_t *result_pool,
           apr_pool_t *scratch_pool);
 
-#ifndef SVN_WC__OP_DEPTH
-static svn_error_t *
-elide_copyfrom(svn_wc__db_pdh_t *pdh,
-               const char *local_relpath,
-               apr_pool_t *scratch_pool);
-#endif
-
 static svn_error_t *
 scan_addition(svn_wc__db_status_t *status,
               const char **op_root_relpath,
@@ -299,7 +292,6 @@ scan_addition(svn_wc__db_status_t *statu
 
 static svn_error_t *
 scan_deletion(const char **base_del_relpath,
-              svn_boolean_t *base_replaced,
               const char **moved_to_relpath,
               const char **work_del_relpath,
               svn_wc__db_pdh_t *pdh,
@@ -311,6 +303,14 @@ static svn_error_t *
 convert_to_working_status(svn_wc__db_status_t *working_status,
                           svn_wc__db_status_t status);
 
+static svn_error_t *
+wclock_owns_lock(svn_boolean_t *own_lock,
+                 svn_wc__db_pdh_t *pdh,
+                 const char *local_relpath,
+                 svn_boolean_t exact,
+                 apr_pool_t *scratch_pool);
+
+
 /* Return the absolute path, in local path style, of LOCAL_RELPATH in WCROOT. */
 static const char *
 path_for_error_message(const svn_wc__db_wcroot_t *wcroot,
@@ -1252,73 +1252,6 @@ which_trees_exist(svn_boolean_t *base_ex
 }
 
 
-/* Determine which trees' nodes exist for a given LOCAL_RELPATH in the
-   specified SDB.
-
-   Note: this is VERY similar to the above which_trees_exist() except that
-   we return a WC_ID and verify some additional constraints.  */
-static svn_error_t *
-prop_upgrade_trees(svn_boolean_t *base_exists,
-                   svn_wc__db_status_t *base_presence,
-                   svn_boolean_t *working_exists,
-                   svn_wc__db_status_t *work_presence,
-                   apr_int64_t *wc_id,
-                   svn_sqlite__db_t *sdb,
-                   const char *local_relpath)
-{
-  svn_sqlite__stmt_t *stmt;
-  svn_boolean_t have_row;
-
-  *base_exists = FALSE;
-  *working_exists = FALSE;
-
-  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_PLAN_PROP_UPGRADE));
-  SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
-  SVN_ERR(svn_sqlite__step(&have_row, stmt));
-
-  /* During a property upgrade, there better be a row corresponding to
-     the provided LOCAL_RELPATH. We shouldn't even be here without a
-     query for available rows.  */
-  SVN_ERR_ASSERT(have_row);
-
-  /* Use the first column to detect which table this row came from.  */
-  if (svn_sqlite__column_int(stmt, 0))
-    {
-      *working_exists = TRUE;  /* value == 1  */
-      *work_presence = svn_sqlite__column_token(stmt, 1, presence_map);
-    }
-  else
-    {
-      *base_exists = TRUE;  /* value == 0  */
-      *base_presence = svn_sqlite__column_token(stmt, 1, presence_map);
-    }
-
-  /* Return the WC_ID that was assigned.  */
-  *wc_id = svn_sqlite__column_int64(stmt, 2);
-
-  SVN_ERR(svn_sqlite__step(&have_row, stmt));
-  if (have_row)
-    {
-      /* If both rows, then both tables.  */
-      *base_exists = TRUE;
-      *working_exists = TRUE;
-
-      /* If the second row came from WORKING_NODE, then we should also
-         fetch the 'presence' column value.  */
-      if (svn_sqlite__column_int(stmt, 0))
-        *work_presence = svn_sqlite__column_token(stmt, 1, presence_map);
-      else
-        *base_presence = svn_sqlite__column_token(stmt, 1, presence_map);
-
-      /* During an upgrade, there should be just one working copy, so both
-         rows should refer to the same value.  */
-      SVN_ERR_ASSERT(*wc_id == svn_sqlite__column_int64(stmt, 2));
-    }
-
-  return svn_error_return(svn_sqlite__reset(stmt));
-}
-
-
 /* */
 static svn_error_t *
 create_db(svn_sqlite__db_t **sdb,
@@ -2942,7 +2875,7 @@ get_info_for_copy(apr_int64_t *copyfrom_
     {
       const char *base_del_relpath, *work_del_relpath;
 
-      SVN_ERR(scan_deletion(&base_del_relpath, NULL, NULL, &work_del_relpath,
+      SVN_ERR(scan_deletion(&base_del_relpath, NULL, &work_del_relpath,
                             pdh, local_relpath, scratch_pool, scratch_pool));
       if (work_del_relpath)
         {
@@ -3128,11 +3061,6 @@ db_op_copy(svn_wc__db_pdh_t *src_pdh,
                             scratch_pool));
     }
 
-#ifndef SVN_WC__OP_DEPTH
-  /* ### Should do this earlier and insert the node with the right values. */
-  SVN_ERR(elide_copyfrom(dst_pdh, dst_relpath, scratch_pool));
-#endif
-
   SVN_ERR(add_work_items(dst_pdh->wcroot->sdb, work_items, scratch_pool));
 
   return SVN_NO_ERROR;
@@ -3239,7 +3167,6 @@ op_depth_for_copy(apr_int64_t *op_depth,
                   const char *local_relpath,
                   apr_pool_t *scratch_pool)
 {
-#ifdef SVN_WC__OP_DEPTH
   const char *parent_relpath, *name;
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
@@ -3295,9 +3222,6 @@ op_depth_for_copy(apr_int64_t *op_depth,
     }
   SVN_ERR(svn_sqlite__reset(stmt));
   
-#else
-  *op_depth = 2; /* temporary op_depth */
-#endif
   return SVN_NO_ERROR;
 }
 
@@ -3548,11 +3472,7 @@ svn_wc__db_op_add_directory(svn_wc__db_t
   iwb.kind = svn_wc__db_kind_dir;
   iwb.wc_id = pdh->wcroot->wc_id;
   iwb.local_relpath = local_relpath;
-#ifdef SVN_WC__OP_DEPTH
   iwb.op_depth = relpath_depth(local_relpath);
-#else
-  iwb.op_depth = 2;  /* ### temporary op_depth */
-#endif
 
   iwb.work_items = work_items;
 
@@ -3588,11 +3508,7 @@ svn_wc__db_op_add_file(svn_wc__db_t *db,
   iwb.kind = svn_wc__db_kind_file;
   iwb.wc_id = pdh->wcroot->wc_id;
   iwb.local_relpath = local_relpath;
-#ifdef SVN_WC__OP_DEPTH
   iwb.op_depth = relpath_depth(local_relpath);
-#else
-  iwb.op_depth = 2;  /* ### temporary op_depth */
-#endif
 
   iwb.work_items = work_items;
 
@@ -3630,11 +3546,7 @@ svn_wc__db_op_add_symlink(svn_wc__db_t *
   iwb.kind = svn_wc__db_kind_symlink;
   iwb.wc_id = pdh->wcroot->wc_id;
   iwb.local_relpath = local_relpath;
-#ifdef SVN_WC__OP_DEPTH
   iwb.op_depth = relpath_depth(local_relpath);
-#else
-  iwb.op_depth = 2;  /* ### temporary op_depth */
-#endif
 
   iwb.target = target;
 
@@ -4488,7 +4400,6 @@ db_working_update_presence(apr_int64_t o
     {
       /* Switching to base-deleted is undoing an add/copy.  By this
          stage an add will have no children. */
-#ifdef SVN_WC__OP_DEPTH
       const apr_array_header_t *children;
       apr_pool_t *iterpool;
       int i;
@@ -4527,14 +4438,6 @@ db_working_update_presence(apr_int64_t o
             SVN_ERR(db_working_actual_remove(pdh, child_relpath, iterpool));
         }
       svn_pool_destroy(iterpool);
-#else
-      /* If this was a copy then any children of the copy will now be
-         not-present and should be removed. */
-      SVN_ERR(remove_children(pdh, local_relpath,
-                              svn_wc__db_status_not_present,
-                              2 /* ### temporary op_depth */,
-                              scratch_pool));
-#endif
 
       /* Reset the copyfrom in case this was a copy.
          ### What else should be reset? Properties? Or copy the node again? */
@@ -4590,10 +4493,6 @@ db_working_actual_remove(svn_wc__db_pdh_
   SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath));
   SVN_ERR(svn_sqlite__step_done(stmt));
 
-#ifndef SVN_WC__OP_DEPTH
-  SVN_ERR(remove_children(pdh, local_relpath, svn_wc__db_status_not_present,
-                          2 /* ### temporary op_depth */, scratch_pool));
-#else
   SVN_ERR(remove_children(pdh, local_relpath, svn_wc__db_status_base_deleted,
                           op_depth + 1, scratch_pool));
   SVN_ERR(remove_children(pdh, local_relpath, svn_wc__db_status_normal,
@@ -4602,7 +4501,6 @@ db_working_actual_remove(svn_wc__db_pdh_
                           op_depth, scratch_pool));
   SVN_ERR(remove_children(pdh, local_relpath, svn_wc__db_status_incomplete,
                           op_depth, scratch_pool));
-#endif
 
   /* Postcondition: There are no NODES rows in this subtree, at same or
    * greater op_depth. */
@@ -4646,25 +4544,15 @@ db_working_insert(svn_wc__db_status_t st
                   apr_pool_t *scratch_pool)
 {
   svn_sqlite__stmt_t *stmt;
-#ifdef SVN_WC__OP_DEPTH
   const char *like_arg = construct_like_arg(local_relpath, scratch_pool);
   apr_int64_t op_depth = relpath_depth(local_relpath);
-#else
-  apr_int64_t op_depth = 2; /* ### temporary op_depth */
-#endif
 
-#ifdef SVN_WC__OP_DEPTH
   SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
                                     STMT_INSERT_WORKING_NODE_FROM_NODE));
-#else
-  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
-                                    STMT_INSERT_WORKING_NODE_FROM_BASE));
-#endif
   SVN_ERR(svn_sqlite__bindf(stmt, "isit", pdh->wcroot->wc_id,
                             local_relpath, op_depth, presence_map, status));
   SVN_ERR(svn_sqlite__insert(NULL, stmt));
 
-#ifdef SVN_WC__OP_DEPTH
   /* Need to update the op_depth of all deleted child trees -- this
      relies on the recursion having already deleted the trees so
      that they are all at op_depth+1.
@@ -4679,7 +4567,6 @@ db_working_insert(svn_wc__db_status_t st
   SVN_ERR(svn_sqlite__bindf(stmt, "isi",
                             pdh->wcroot->wc_id, like_arg, op_depth));
   SVN_ERR(svn_sqlite__update(NULL, stmt));
-#endif
 
   return SVN_NO_ERROR;
 }
@@ -4868,9 +4755,6 @@ temp_op_delete_txn(void *baton, svn_sqli
   struct temp_op_delete_baton *b = baton;
   svn_wc__db_status_t status, new_working_status;
   svn_boolean_t have_work, add_work = FALSE, del_work = FALSE, mod_work = FALSE;
-#ifndef SVN_WC__OP_DEPTH
-  svn_boolean_t new_have_work;
-#endif
 
   SVN_ERR(read_info(&status,
                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
@@ -4880,23 +4764,13 @@ temp_op_delete_txn(void *baton, svn_sqli
                     b->pdh, b->local_relpath,
                     scratch_pool, scratch_pool));
 
-#ifndef SVN_WC__OP_DEPTH
-  new_have_work = have_work;
-  new_working_status = status;
-#endif
-
   if (!have_work)
     {
       /* No structural changes  */
       if (status == svn_wc__db_status_normal
           || status == svn_wc__db_status_incomplete)
         {
-#ifdef SVN_WC__OP_DEPTH
           add_work = TRUE;
-#else
-          new_have_work = TRUE;
-          new_working_status = svn_wc__db_status_base_deleted;
-#endif
         }
     }
   else if (status == svn_wc__db_status_added)
@@ -4914,26 +4788,15 @@ temp_op_delete_txn(void *baton, svn_sqli
           SVN_ERR(info_below_working(&below_base, &below_work, &below_status,
                                      b->pdh, b->local_relpath, scratch_pool));
 
-#ifdef SVN_WC__OP_DEPTH
           if ((below_base || below_work)
               && below_status != svn_wc__db_status_not_present)
             mod_work = TRUE;
           else
             del_work = TRUE;
-#else
-          if (below_base && below_status != svn_wc__db_status_not_present)
-            new_working_status = svn_wc__db_status_base_deleted;
-          else
-            new_have_work = FALSE;
-#endif
         }
       else
         {
-#ifdef SVN_WC__OP_DEPTH
           add_work = TRUE;
-#else
-          new_working_status = svn_wc__db_status_not_present;
-#endif
         }
     }
   else if (status == svn_wc__db_status_incomplete)
@@ -4941,23 +4804,12 @@ temp_op_delete_txn(void *baton, svn_sqli
       svn_boolean_t add_or_root_of_copy;
       SVN_ERR(is_add_or_root_of_copy(&add_or_root_of_copy,
                                      b->pdh, b->local_relpath, scratch_pool));
-#ifdef SVN_WC__OP_DEPTH
       if (add_or_root_of_copy)
         del_work = TRUE;
       else
         add_work = TRUE;
-#else
-      if (add_or_root_of_copy)
-        new_have_work = FALSE;
-#endif
     }
 
-#ifndef SVN_WC__OP_DEPTH
-  del_work = !new_have_work && have_work;
-  add_work = new_have_work && !have_work;
-  mod_work = new_have_work && have_work && new_working_status != status;
-#endif
-
   if (del_work)
     {
       SVN_ERR(db_working_actual_remove(b->pdh, b->local_relpath, scratch_pool));
@@ -4968,17 +4820,13 @@ temp_op_delete_txn(void *baton, svn_sqli
     }
   else if (add_work)
     {
-#ifdef SVN_WC__OP_DEPTH
       new_working_status = svn_wc__db_status_base_deleted;
-#endif
       SVN_ERR(db_working_insert(new_working_status, b->pdh, b->local_relpath,
                                 scratch_pool));
     }
   else if (mod_work)
     {
-#ifdef SVN_WC__OP_DEPTH
       new_working_status = svn_wc__db_status_base_deleted;
-#endif
       SVN_ERR(db_working_update_presence(relpath_depth(b->local_relpath),
                                          new_working_status, b->pdh,
                                          b->local_relpath, scratch_pool));
@@ -5254,8 +5102,7 @@ read_info(svn_wc__db_status_t *status,
               op_depth = svn_sqlite__column_int64(stmt_info, 0);
             }
 
-          if (have_base)
-            *have_base = (op_depth == 0);
+          *have_base = (op_depth == 0);
         }
     }
   else if (have_act)
@@ -5606,6 +5453,61 @@ svn_wc__db_read_children_info(apr_hash_t
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_wc__db_read_children_walker_info(apr_hash_t **nodes,
+                                     svn_wc__db_t *db,
+                                     const char *dir_abspath,
+                                     apr_pool_t *result_pool,
+                                     apr_pool_t *scratch_pool)
+{
+  svn_wc__db_pdh_t *pdh;
+  const char *dir_relpath;
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+  apr_int64_t op_depth;
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
+
+  SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &dir_relpath, db,
+                                             dir_abspath,
+                                             svn_sqlite__mode_readonly,
+                                             scratch_pool, scratch_pool));
+  VERIFY_USABLE_PDH(pdh);
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
+                                    STMT_SELECT_NODE_CHILDREN_WALKER_INFO));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, dir_relpath));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+  *nodes = apr_hash_make(result_pool);
+  while (have_row)
+    {
+      struct svn_wc__db_walker_info_t *child;
+      const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
+      const char *name = svn_relpath_basename(child_relpath, NULL);
+      svn_error_t *err;
+
+      child = apr_hash_get(*nodes, name, APR_HASH_KEY_STRING);
+      if (child == NULL)
+        child = apr_palloc(result_pool, sizeof(*child));
+
+      op_depth = svn_sqlite__column_int(stmt, 1);
+      child->status = svn_sqlite__column_token(stmt, 2, presence_map);
+      if (op_depth > 0)
+        SVN_ERR(convert_to_working_status(&child->status, child->status));
+      child->kind = svn_sqlite__column_token(stmt, 3, kind_map);
+      apr_hash_set(*nodes, apr_pstrdup(result_pool, name),
+                   APR_HASH_KEY_STRING, child);
+
+      err = svn_sqlite__step(&have_row, stmt);
+      if (err)
+        SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
+    }
+
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  return SVN_NO_ERROR;
+}
 
 svn_error_t *
 svn_wc__db_read_prop(const svn_string_t **propval,
@@ -5917,7 +5819,7 @@ svn_wc__db_global_relocate(svn_wc__db_t 
       if (status == svn_wc__db_status_deleted)
         {
           const char *work_del_relpath;
-          SVN_ERR(scan_deletion(NULL, NULL, NULL, &work_del_relpath,
+          SVN_ERR(scan_deletion(NULL, NULL, &work_del_relpath,
                                 pdh, local_dir_relpath,
                                 scratch_pool, scratch_pool));
           if (work_del_relpath)
@@ -6404,35 +6306,15 @@ struct record_baton {
 };
 
 
-/* Record TRANSLATED_SIZE and LAST_MOD_TIME into the WORKING tree if a
-   node is present; otherwise, record it into the BASE tree. One of them
-   must exist.  */
+/* Record TRANSLATED_SIZE and LAST_MOD_TIME into top layer in NODES */
 static svn_error_t *
 record_fileinfo(void *baton, svn_sqlite__db_t *sdb, apr_pool_t *scratch_pool)
 {
   struct record_baton *rb = baton;
-  svn_boolean_t base_exists;
-  svn_boolean_t working_exists;
   svn_sqlite__stmt_t *stmt;
   int affected_rows;
 
-  SVN_ERR(which_trees_exist(&base_exists, &working_exists,
-                            sdb, rb->wcroot->wc_id, rb->local_relpath));
-  if (!base_exists && !working_exists)
-    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
-                             _("Could not find node '%s' for recording file "
-                               "information."),
-                             path_for_error_message(rb->wcroot,
-                                                    rb->local_relpath,
-                                                    scratch_pool));
-
-  /* ### Instead of doing it this way, we just ought to update the highest
-     op_depth level. That way, there's no need to find out which
-     tree to update first */
-  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
-                                    working_exists
-                                      ? STMT_UPDATE_WORKING_NODE_FILEINFO
-                                      : STMT_UPDATE_BASE_NODE_FILEINFO));
+  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_UPDATE_NODE_FILEINFO));
   SVN_ERR(svn_sqlite__bindf(stmt, "isii",
                             rb->wcroot->wc_id, rb->local_relpath,
                             rb->translated_size, rb->last_mod_time));
@@ -6611,10 +6493,6 @@ scan_addition(svn_wc__db_status_t *statu
   const char *current_relpath = local_relpath;
   const char *build_relpath = "";
   svn_wc__db_wcroot_t *wcroot = pdh->wcroot;
-#ifndef SVN_WC__OP_DEPTH
-  svn_boolean_t found_info = FALSE;
-  const char *child_relpath = NULL;
-#endif
 
   /* Initialize most of the OUT parameters. Generally, we'll only be filling
      in a subset of these, so it is easier to init all up front. Note that
@@ -6629,125 +6507,6 @@ scan_addition(svn_wc__db_status_t *statu
   if (original_revision)
     *original_revision = SVN_INVALID_REVNUM;
 
-
-#ifndef SVN_WC__OP_DEPTH
-  while (TRUE)
-    {
-      svn_sqlite__stmt_t *stmt;
-      svn_boolean_t have_row;
-      svn_wc__db_status_t presence;
-
-      /* ### is it faster to fetch fewer columns? */
-      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                        STMT_SELECT_WORKING_NODE));
-      SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
-      SVN_ERR(svn_sqlite__step(&have_row, stmt));
-
-      if (!have_row)
-        {
-          /* Reset statement before (optionally) returning */
-          SVN_ERR(svn_sqlite__reset(stmt));
-
-          if (current_relpath == local_relpath)
-            /* ### maybe we should return a usage error instead?  */
-            return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
-                                     _("The node '%s' was not found."),
-                                     path_for_error_message(wcroot,
-                                                            local_relpath,
-                                                            scratch_pool));
-
-          /* We just fell off the top of the WORKING tree. If we haven't
-             found the operation root, then the child node that we just
-             left was that root.  */
-          if (op_root_relpath && *op_root_relpath == NULL)
-            {
-              SVN_ERR_ASSERT(child_relpath != NULL);
-              *op_root_relpath = apr_pstrdup(result_pool, child_relpath);
-            }
-
-          /* This node was added/copied/moved and has an implicit location
-             in the repository. We now need to traverse BASE nodes looking
-             for repository info.  */
-          break;
-        }
-
-      presence = svn_sqlite__column_token(stmt, 1, presence_map);
-
-      /* Record information from the starting node.  */
-      if (current_relpath == local_relpath)
-        {
-          /* The starting node should exist normally.  */
-          if (presence != svn_wc__db_status_normal)
-            return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
-                                     svn_sqlite__reset(stmt),
-                                     _("Expected node '%s' to be added."),
-                                     path_for_error_message(wcroot,
-                                                            local_relpath,
-                                                            scratch_pool));
-
-          /* Provide the default status; we'll override as appropriate. */
-          if (status)
-            *status = svn_wc__db_status_added;
-        }
-
-      /* We want the operation closest to the start node, and then we
-         ignore any operations on its ancestors.  */
-      if (!found_info
-          && presence == svn_wc__db_status_normal
-          && !svn_sqlite__column_is_null(stmt, 10 /* copyfrom_repos_id */))
-        {
-          if (status)
-            {
-              if (svn_sqlite__column_boolean(stmt, 13 /* moved_here */))
-                *status = svn_wc__db_status_moved_here;
-              else
-                *status = svn_wc__db_status_copied;
-            }
-          if (op_root_relpath)
-            *op_root_relpath = apr_pstrdup(result_pool, current_relpath);
-          if (original_repos_relpath)
-            *original_repos_relpath = svn_sqlite__column_text(stmt, 11,
-                                                              result_pool);
-          if (original_repos_id)
-            *original_repos_id = svn_sqlite__column_int64(stmt, 10);
-          if (original_revision)
-            *original_revision = svn_sqlite__column_revnum(stmt, 12);
-
-          /* We may have to keep tracking upwards for REPOS_* values.
-             If they're not needed, then just return.  */
-          if (repos_relpath == NULL
-              && repos_id == NULL)
-            return svn_error_return(svn_sqlite__reset(stmt));
-
-          /* We've found the info we needed. Scan for the top of the
-             WORKING tree, and then the REPOS_* information.  */
-          found_info = TRUE;
-        }
-
-      SVN_ERR(svn_sqlite__reset(stmt));
-
-      /* If the caller wants to know the starting node's REPOS_RELPATH,
-         then keep track of what we're stripping off the ABSPATH as we
-         traverse up the tree.  */
-      if (repos_relpath)
-        {
-          build_relpath = svn_relpath_join(svn_dirent_basename(current_relpath,
-                                                               NULL),
-                                           build_relpath,
-                                           scratch_pool);
-        }
-
-      /* Move to the parent node. Remember the abspath to this node, since
-         it could be the root of an add/delete.  */
-      child_relpath = current_relpath;
-
-      /* The wcroot can't have a restructuring operation; make sure we don't
-         loop on invalid data */
-      SVN_ERR_ASSERT(current_relpath[0] != '\0');
-
-      current_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
-    }
-#else  /* ifdef SVN_WC__OP_DEPTH */
   {
     svn_sqlite__stmt_t *stmt;
     svn_boolean_t have_row;
@@ -6911,7 +6670,7 @@ scan_addition(svn_wc__db_status_t *statu
 
     build_relpath = repos_prefix_path;
   }
-#endif
+
   /* If we're here, then we have an added/copied/moved (start) node, and
      CURRENT_ABSPATH now points to a BASE node. Figure out the repository
      information for the current node, and use that to compute the start
@@ -7019,7 +6778,6 @@ svn_wc__db_scan_addition(svn_wc__db_stat
  * DB+LOCAL_ABSPATH, and outputting relpaths instead of abspaths. */
 static svn_error_t *
 scan_deletion(const char **base_del_relpath,
-              svn_boolean_t *base_replaced,
               const char **moved_to_relpath,
               const char **work_del_relpath,
               svn_wc__db_pdh_t *pdh,
@@ -7033,15 +6791,11 @@ scan_deletion(const char **base_del_relp
   svn_boolean_t child_has_base = FALSE;
   svn_boolean_t found_moved_to = FALSE;
   svn_wc__db_wcroot_t *wcroot = pdh->wcroot;
-#ifdef SVN_WC__OP_DEPTH
   apr_int64_t local_op_depth, op_depth;
-#endif
 
   /* Initialize all the OUT parameters.  */
   if (base_del_relpath != NULL)
     *base_del_relpath = NULL;
-  if (base_replaced != NULL)
-    *base_replaced = FALSE;  /* becomes TRUE when we know for sure.  */
   if (moved_to_relpath != NULL)
     *moved_to_relpath = NULL;
   if (work_del_relpath != NULL)
@@ -7164,16 +6918,6 @@ scan_deletion(const char **base_del_relp
           /* If a BASE node is marked as not-present, then we'll ignore
              it within this function. That status is simply a bookkeeping
              gimmick, not a real node that may have been deleted.  */
-
-          /* If we're looking at a present BASE node, *and* there is a
-             WORKING node (present or deleted), then a replacement has
-             occurred here or in an ancestor.  */
-          if (base_replaced != NULL
-              && base_presence == svn_wc__db_status_normal
-              && work_presence != svn_wc__db_status_base_deleted)
-            {
-              *base_replaced = TRUE;
-            }
         }
 
       /* Only grab the nearest ancestor.  */
@@ -7195,7 +6939,6 @@ scan_deletion(const char **base_del_relp
                                     svn_sqlite__column_text(stmt, 2, NULL));
         }
 
-#ifdef SVN_WC__OP_DEPTH
       op_depth = svn_sqlite__column_int64(stmt, 3);
       if (current_relpath == local_relpath)
         local_op_depth = op_depth;
@@ -7203,13 +6946,6 @@ scan_deletion(const char **base_del_relp
       if (work_del_relpath && !work_del_relpath[0]
           && ((op_depth < local_op_depth && op_depth > 0)
               || child_presence == svn_wc__db_status_not_present))
-#else
-      if (work_del_relpath != NULL
-          && work_presence == svn_wc__db_status_normal
-          && child_presence == svn_wc__db_status_not_present)
-          /* Parent is normal, but child was deleted. Therefore, the child
-             is the root of a WORKING subtree deletion.  */
-#endif
         {
           *work_del_relpath = apr_pstrdup(result_pool, child_relpath);
         }
@@ -7236,7 +6972,6 @@ scan_deletion(const char **base_del_relp
 
 svn_error_t *
 svn_wc__db_scan_deletion(const char **base_del_abspath,
-                         svn_boolean_t *base_replaced,
                          const char **moved_to_abspath,
                          const char **work_del_abspath,
                          svn_wc__db_t *db,
@@ -7255,7 +6990,7 @@ svn_wc__db_scan_deletion(const char **ba
                               scratch_pool, scratch_pool));
   VERIFY_USABLE_PDH(pdh);
 
-  SVN_ERR(scan_deletion(&base_del_relpath, base_replaced, &moved_to_relpath,
+  SVN_ERR(scan_deletion(&base_del_relpath, &moved_to_relpath,
                         &work_del_relpath,
                         pdh, local_relpath, result_pool, scratch_pool));
 
@@ -7355,12 +7090,10 @@ svn_wc__db_upgrade_apply_props(svn_sqlit
                                int original_format,
                                apr_pool_t *scratch_pool)
 {
-  svn_boolean_t have_base;
-  svn_wc__db_status_t base_presence;
-  svn_boolean_t have_work;
-  svn_wc__db_status_t work_presence;
-  apr_int64_t wc_id;
   svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+  apr_int64_t wc_id, top_op_depth = -1, below_op_depth = -1;
+  svn_wc__db_status_t top_presence, below_presence;
   int affected_rows;
 
   /* ### working_props: use set_props_txn.
@@ -7390,19 +7123,31 @@ svn_wc__db_upgrade_apply_props(svn_sqlit
      the handling of our inputs, relative to the state of this node.
   */
 
-  /* Collect information about this node.  */
-  SVN_ERR(prop_upgrade_trees(&have_base, &base_presence,
-                             &have_work, &work_presence,
-                             &wc_id, sdb, local_relpath));
+  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_NODE_UPGRADE));
+  SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+  if (have_row)
+    {
+      top_op_depth = svn_sqlite__column_int64(stmt, 0);
+      top_presence = svn_sqlite__column_token(stmt, 1, presence_map);
+      wc_id = svn_sqlite__column_int64(stmt, 2);
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+      if (have_row)
+        {
+          below_op_depth = svn_sqlite__column_int64(stmt, 0);
+          below_presence = svn_sqlite__column_token(stmt, 1, presence_map);
+        }
+    }
+  SVN_ERR(svn_sqlite__reset(stmt));
 
   /* Detect the buggy scenario described above. We cannot upgrade this
      working copy if we have no idea where BASE_PROPS should go.  */
   if (original_format > SVN_WC__NO_REVERT_FILES
       && revert_props == NULL
-      && have_work
-      && work_presence == svn_wc__db_status_normal
-      && have_base
-      && base_presence != svn_wc__db_status_not_present)
+      && top_op_depth != -1
+      && top_presence == svn_wc__db_status_normal
+      && below_op_depth != -1
+      && below_presence != svn_wc__db_status_not_present)
     {
       /* There should be REVERT_PROPS, so it appears that we just ran into
          the described bug. Sigh.  */
@@ -7415,48 +7160,45 @@ svn_wc__db_upgrade_apply_props(svn_sqlit
                                                  scratch_pool), scratch_pool));
     }
 
-  if (have_base
-      && (base_presence == svn_wc__db_status_normal
-          || base_presence == svn_wc__db_status_incomplete))
-    {
-      apr_hash_t *props = revert_props ? revert_props : base_props;
+  /* Need at least one row, or two rows if there are revert props */
+  if (top_op_depth == -1
+      || (below_op_depth == -1 && revert_props))
+    return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
+                             _("Insufficient NODES rows for '%s'"),
+                             svn_dirent_local_style(
+                               svn_dirent_join(dir_abspath, local_relpath,
+                                               scratch_pool), scratch_pool));
+
+  /* one row, base props only: upper row gets base props
+     two rows, base props only: lower row gets base props
+     two rows, revert props only: lower row gets revert props
+     two rows, base and revert props: upper row gets base, lower gets revert */
 
+
+  if (revert_props || below_op_depth == -1)
+    {
       SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
-                                        STMT_UPDATE_NODE_BASE_PROPS));
-      SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
-      SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
+                                        STMT_UPDATE_NODE_PROPS));
+      SVN_ERR(svn_sqlite__bindf(stmt, "isi",
+                                wc_id, local_relpath, top_op_depth));
+      SVN_ERR(svn_sqlite__bind_properties(stmt, 4, base_props, scratch_pool));
       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
 
-      /* ### should we provide a nicer error message?  */
       SVN_ERR_ASSERT(affected_rows == 1);
     }
 
-  if (have_work)
+  if (below_op_depth != -1)
     {
-      /* WORKING_NODE has very limited 'presence' values.  */
-      SVN_ERR_ASSERT(work_presence == svn_wc__db_status_normal
-                     || work_presence == svn_wc__db_status_not_present
-                     || work_presence == svn_wc__db_status_base_deleted
-                     || work_presence == svn_wc__db_status_incomplete);
+      apr_hash_t *props = revert_props ? revert_props : base_props;
 
-      /* Do we have a replaced node? It has properties: an empty set for
-         adds, and a non-empty set for copies/moves.  */
-      if (original_format > SVN_WC__NO_REVERT_FILES
-          && (work_presence == svn_wc__db_status_normal
-              || work_presence == svn_wc__db_status_incomplete))
-        {
-          SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
-                                            STMT_UPDATE_NODE_WORKING_PROPS));
-          SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
-          SVN_ERR(svn_sqlite__bind_properties(stmt, 3, base_props,
-                                              scratch_pool));
-          SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
+      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
+                                        STMT_UPDATE_NODE_PROPS));
+      SVN_ERR(svn_sqlite__bindf(stmt, "isi",
+                                wc_id, local_relpath, below_op_depth));
+      SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
+      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
 
-          /* ### should we provide a nicer error message?  */
-          SVN_ERR_ASSERT(affected_rows == 1);
-        }
-      /* else other states should have no properties.  */
-      /* ### should we insert empty props for <= SVN_WC__NO_REVERT_FILES?  */
+      SVN_ERR_ASSERT(affected_rows == 1);
     }
 
   /* If there are WORKING_PROPS, then they always go into ACTUAL_NODE.  */
@@ -8210,7 +7952,6 @@ svn_wc__db_temp_wcroot_tempdir(const cha
 /* Baton for wclock_obtain_cb() */
 struct wclock_obtain_baton
 {
-  svn_wc__db_t *db;
   svn_wc__db_pdh_t *pdh;
   const char *local_relpath;
   int levels_to_lock;
@@ -8284,7 +8025,6 @@ wclock_obtain_cb(void *baton,
 
   while (got_row)
     {
-      const char *lock_abspath;
       svn_boolean_t own_lock;
 
       lock_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
@@ -8298,13 +8038,10 @@ wclock_obtain_cb(void *baton,
           continue;
         }
 
-      lock_abspath = svn_dirent_join(wcroot->abspath,
-                                     lock_relpath, scratch_pool);
-
       /* Check if we are the lock owner, because we should be able to
          extend our lock. */
-      err = svn_wc__db_wclock_owns_lock(&own_lock, bt->db, lock_abspath,
-                                        TRUE, scratch_pool);
+      err = wclock_owns_lock(&own_lock, bt->pdh, lock_relpath,
+                             TRUE, scratch_pool);
 
       if (err)
         SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
@@ -8314,7 +8051,7 @@ wclock_obtain_cb(void *baton,
           SVN_ERR(svn_sqlite__reset(stmt));
           err = svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
                                    _("'%s' is already locked."),
-                                   svn_dirent_local_style(lock_abspath,
+                                   path_for_error_message(wcroot, lock_relpath,
                                                           scratch_pool));
           return svn_error_createf(SVN_ERR_WC_LOCKED, err,
                                    _("Working copy '%s' locked."),
@@ -8441,22 +8178,16 @@ svn_wc__db_wclock_obtain(svn_wc__db_t *d
                   || (lock->levels + relpath_depth(lock->local_relpath))
                             >= depth))
             {
-              const char *lock_abspath
-                  = svn_dirent_join(baton.pdh->wcroot->abspath,
-                                    lock->local_relpath,
-                                    scratch_pool);
-
-              return svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
-                                       _("'%s' is already locked via '%s'."),
-                                       svn_dirent_local_style(local_abspath,
-                                                              scratch_pool),
-                                       svn_dirent_local_style(lock_abspath,
-                                                              scratch_pool));
+              return svn_error_createf(
+                SVN_ERR_WC_LOCKED, NULL,
+                _("'%s' is already locked via '%s'."),
+                svn_dirent_local_style(local_abspath, scratch_pool),
+                path_for_error_message(baton.pdh->wcroot, lock->local_relpath,
+                                       scratch_pool));
             }
         }
     }
 
-  baton.db = db;
   baton.steal_lock = steal_lock;
   baton.levels_to_lock = levels_to_lock;
 
@@ -8585,29 +8316,18 @@ svn_wc__db_wclock_release(svn_wc__db_t *
   return SVN_NO_ERROR;
 }
 
-svn_error_t *
-svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
-                            svn_wc__db_t *db,
-                            const char *local_abspath,
-                            svn_boolean_t exact,
-                            apr_pool_t *scratch_pool)
+/* Like svn_wc__db_wclock_owns_lock() but taking PDH+LOCAL_RELPATH instead
+ * of DB+LOCAL_ABSPATH. */
+static svn_error_t *
+wclock_owns_lock(svn_boolean_t *own_lock,
+                 svn_wc__db_pdh_t *pdh,
+                 const char *local_relpath,
+                 svn_boolean_t exact,
+                 apr_pool_t *scratch_pool)
 {
-  svn_wc__db_pdh_t *pdh;
-  const char *local_relpath;
   apr_array_header_t *owned_locks;
   int lock_level, i;
 
-  SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &local_relpath, db,
-                              local_abspath, svn_sqlite__mode_readwrite,
-                              scratch_pool, scratch_pool));
-
-  if (!pdh->wcroot)
-    return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
-                             _("The node '%s' was not found."),
-                             svn_dirent_local_style(local_abspath,
-                                                    scratch_pool));
-
-  VERIFY_USABLE_PDH(pdh);
   *own_lock = FALSE;
   owned_locks = pdh->wcroot->owned_locks;
   lock_level = relpath_depth(local_relpath);
@@ -8644,6 +8364,33 @@ svn_wc__db_wclock_owns_lock(svn_boolean_
 }
 
 svn_error_t *
+svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
+                            svn_wc__db_t *db,
+                            const char *local_abspath,
+                            svn_boolean_t exact,
+                            apr_pool_t *scratch_pool)
+{
+  svn_wc__db_pdh_t *pdh;
+  const char *local_relpath;
+
+  SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &local_relpath, db,
+                              local_abspath, svn_sqlite__mode_readwrite,
+                              scratch_pool, scratch_pool));
+
+  if (!pdh->wcroot)
+    return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
+                             _("The node '%s' was not found."),
+                             svn_dirent_local_style(local_abspath,
+                                                    scratch_pool));
+
+  VERIFY_USABLE_PDH(pdh);
+
+  SVN_ERR(wclock_owns_lock(own_lock, pdh, local_relpath, exact, scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
 svn_wc__db_temp_op_set_base_incomplete(svn_wc__db_t *db,
                                        const char *local_dir_abspath,
                                        svn_boolean_t incomplete,
@@ -8807,11 +8554,7 @@ make_copy_txn(void *baton,
   struct make_copy_baton *mcb = baton;
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
-#ifdef SVN_WC__OP_DEPTH
   svn_boolean_t add_working_base_deleted = FALSE;
-#else
-  svn_boolean_t add_working = TRUE;
-#endif
   svn_boolean_t remove_working = FALSE;
   const apr_array_header_t *children;
   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
@@ -8839,11 +8582,7 @@ make_copy_txn(void *baton,
         /* Make existing deletions of BASE_NODEs remove WORKING_NODEs */
         remove_working = TRUE;
 
-#ifdef SVN_WC__OP_DEPTH
       add_working_base_deleted = TRUE;
-#else
-      add_working = FALSE;
-#endif
     }
   else
     SVN_ERR(svn_sqlite__reset(stmt));
@@ -8884,7 +8623,6 @@ make_copy_txn(void *baton,
       SVN_ERR(svn_sqlite__step_done(stmt));
     }
 
-#ifdef SVN_WC__OP_DEPTH
   if (add_working_base_deleted)
     {
       SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
@@ -8897,9 +8635,6 @@ make_copy_txn(void *baton,
       SVN_ERR(svn_sqlite__step_done(stmt));
     }
   else
-#else
-  if (add_working)
-#endif
     {
       SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
                                       STMT_INSERT_WORKING_NODE_FROM_BASE_COPY));
@@ -8955,11 +8690,7 @@ svn_wc__db_temp_op_make_copy(svn_wc__db_
 
   mcb.db = db;
   mcb.local_abspath = local_abspath;
-#ifdef SVN_WC__OP_DEPTH
   mcb.op_depth = relpath_depth(mcb.local_relpath);
-#else
-  mcb.op_depth = 2;  /* ### temporary op_depth */
-#endif
 
   SVN_ERR(svn_sqlite__with_transaction(mcb.pdh->wcroot->sdb,
                                        make_copy_txn, &mcb,
@@ -8968,168 +8699,6 @@ svn_wc__db_temp_op_make_copy(svn_wc__db_
   return SVN_NO_ERROR;
 }
 
-#ifndef SVN_WC__OP_DEPTH
-/* Return the copyfrom info for LOCAL_ABSPATH resolving inheritance. */
-static svn_error_t *
-get_copyfrom(apr_int64_t *copyfrom_repos_id,
-             const char **copyfrom_relpath,
-             svn_revnum_t *copyfrom_revnum,
-             svn_wc__db_pdh_t *pdh,
-             const char *local_relpath,
-             apr_pool_t *result_pool,
-             apr_pool_t *scratch_pool)
-{
-  svn_sqlite__stmt_t *stmt;
-  svn_boolean_t have_row;
-
-  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
-                                    STMT_SELECT_WORKING_NODE));
-  SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath));
-  SVN_ERR(svn_sqlite__step(&have_row, stmt));
-  if (!have_row)
-    {
-      *copyfrom_repos_id = INVALID_REPOS_ID;
-      *copyfrom_relpath = NULL;
-      *copyfrom_revnum = SVN_INVALID_REVNUM;
-      SVN_ERR(svn_sqlite__reset(stmt));
-      return SVN_NO_ERROR;
-    }
-
-  if (svn_sqlite__column_is_null(stmt, 10 /* copyfrom_repos_id */))
-    {
-      /* Resolve inherited copyfrom */
-      const char *parent_relpath, *name, *parent_copyfrom_relpath;
-
-      SVN_ERR(svn_sqlite__reset(stmt));
-      svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool);
-      SVN_ERR(get_copyfrom(copyfrom_repos_id, &parent_copyfrom_relpath,
-                           copyfrom_revnum,
-                           pdh, parent_relpath, scratch_pool, scratch_pool));
-      if (parent_copyfrom_relpath)
-        *copyfrom_relpath = svn_relpath_join(parent_copyfrom_relpath, name,
-                                             result_pool);
-      else
-        *copyfrom_relpath = NULL;
-      return SVN_NO_ERROR;
-    }
-
-  *copyfrom_repos_id = svn_sqlite__column_int64(stmt, 10);
-  *copyfrom_relpath = svn_sqlite__column_text(stmt, 11, result_pool);
-  *copyfrom_revnum = svn_sqlite__column_revnum(stmt, 12);
-
-  SVN_ERR(svn_sqlite__reset(stmt));
-
-  return SVN_NO_ERROR;
-}
-#endif
-
-
-#ifndef SVN_WC__OP_DEPTH
-static svn_error_t *
-elide_copyfrom(svn_wc__db_pdh_t *pdh,
-               const char *local_relpath,
-               apr_pool_t *scratch_pool)
-{
-  svn_sqlite__stmt_t *stmt;
-  svn_boolean_t have_row;
-  apr_int64_t original_repos_id;
-  const char *original_repos_relpath;
-  svn_revnum_t original_revision;
-  const char *parent_local_relpath;
-  const char *name;
-  apr_int64_t parent_repos_id;
-  const char *parent_repos_relpath;
-  svn_revnum_t parent_revision;
-  const char *implied_relpath;
-
-  /* Examine the current WORKING_NODE row's copyfrom information. If there
-     is no WORKING node, then simply exit.  */
-  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
-                                    STMT_SELECT_WORKING_NODE));
-  SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath));
-  SVN_ERR(svn_sqlite__step(&have_row, stmt));
-
-  if (!have_row)
-    return svn_error_return(svn_sqlite__reset(stmt));
-
-  /* Already inheriting copyfrom information?  */
-  if (svn_sqlite__column_is_null(stmt, 10 /* copyfrom_repos_id */))
-    return svn_error_return(svn_sqlite__reset(stmt));
-
-  original_repos_id = svn_sqlite__column_int64(stmt, 10);
-  original_repos_relpath = svn_sqlite__column_text(stmt, 11, scratch_pool);
-  original_revision = svn_sqlite__column_revnum(stmt, 12);
-
-  SVN_ERR(svn_sqlite__reset(stmt));
-
-  svn_dirent_split(&parent_local_relpath, &name, local_relpath, scratch_pool);
-
-  SVN_ERR(get_copyfrom(&parent_repos_id, &parent_repos_relpath,
-                       &parent_revision,
-                       pdh, parent_local_relpath, scratch_pool, scratch_pool));
-  if (parent_revision == SVN_INVALID_REVNUM)
-    return SVN_NO_ERROR;
-
-  /* Now we need to determine if the child's values are derivable from
-     the parent values.  */
-
-  /* If the revision numbers are not the same, then easy exit.
-
-     Note that we *can* have a mixed-rev copied subtree. We don't want
-     to elide the copyfrom information for these cases.  */
-  if (original_revision != parent_revision)
-    return SVN_NO_ERROR;
-
-  /* The child repos_relpath should be under the parent's.  */
-  if (svn_relpath_is_child(parent_repos_relpath,
-                           original_repos_relpath,
-                           NULL) == NULL)
-    return SVN_NO_ERROR;
-
-  /* Given the relpath from OP_ROOT_ABSPATH down to LOCAL_ABSPATH, compute
-     an implied REPOS_RELPATH. If that does not match the RELPATH we found,
-     then we can exit (the child is a new copy root).  */
-  implied_relpath = svn_relpath_join(parent_repos_relpath, name, scratch_pool);
-  if (strcmp(implied_relpath, original_repos_relpath) != 0)
-    return SVN_NO_ERROR;
-
-  if (original_repos_id != parent_repos_id)
-    return SVN_NO_ERROR;
-
-  /* The child's copyfrom information is derivable from the parent.
-     The data should be reset to null, indicating the derivation.  */
-  /* Should probably use the op_depth from query above and simplify this
-     query. */
-  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
-                                    STMT_UPDATE_COPYFROM_TO_INHERIT));
-  SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath));
-  SVN_ERR(svn_sqlite__update(NULL, stmt));
-
-  return SVN_NO_ERROR;
-}
-
-
-svn_error_t *
-svn_wc__db_temp_elide_copyfrom(svn_wc__db_t *db,
-                               const char *local_abspath,
-                               apr_pool_t *scratch_pool)
-{
-  svn_wc__db_pdh_t *pdh;
-  const char *local_relpath;
-
-  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
-
-  SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &local_relpath, db,
-                              local_abspath, svn_sqlite__mode_readwrite,
-                              scratch_pool, scratch_pool));
-  VERIFY_USABLE_PDH(pdh);
-
-  SVN_ERR(elide_copyfrom(pdh, local_relpath, scratch_pool));
-
-  return SVN_NO_ERROR;
-}
-#endif
-
 
 svn_error_t *
 svn_wc__db_temp_get_file_external(const char **serialized_file_external,
@@ -9553,3 +9122,25 @@ svn_wc__db_temp_op_set_new_dir_to_incomp
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_wc__db_temp_below_work(svn_boolean_t *have_work,
+                           svn_wc__db_t *db,
+                           const char *local_abspath,
+                           apr_pool_t *scratch_pool)
+{
+  svn_boolean_t have_base;
+  svn_wc__db_status_t status;
+  svn_wc__db_pdh_t *pdh;
+  const char *local_relpath;
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+
+  SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &local_relpath, db,
+                              local_abspath, svn_sqlite__mode_readwrite,
+                              scratch_pool, scratch_pool));
+  VERIFY_USABLE_PDH(pdh);
+  SVN_ERR(info_below_working(&have_base, have_work, &status,
+                             pdh, local_relpath, scratch_pool));
+
+  return SVN_NO_ERROR;
+}