You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by hw...@apache.org on 2010/09/15 21:32:38 UTC

svn commit: r997472 [28/41] - in /subversion/branches/py-tests-as-modules: ./ build/ build/ac-macros/ build/generator/ build/generator/templates/ contrib/server-side/ notes/ notes/tree-conflicts/ notes/wc-ng/ subversion/bindings/javahl/native/ subversi...

Modified: subversion/branches/py-tests-as-modules/subversion/libsvn_wc/wc_db.c
URL: http://svn.apache.org/viewvc/subversion/branches/py-tests-as-modules/subversion/libsvn_wc/wc_db.c?rev=997472&r1=997471&r2=997472&view=diff
==============================================================================
--- subversion/branches/py-tests-as-modules/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/branches/py-tests-as-modules/subversion/libsvn_wc/wc_db.c Wed Sep 15 19:32:26 2010
@@ -52,8 +52,7 @@
 #include "private/svn_token.h"
 
 
-#define NOT_IMPLEMENTED() \
-  return svn_error__malfunction(TRUE, __FILE__, __LINE__, "Not implemented.")
+#define NOT_IMPLEMENTED() SVN__NOT_IMPLEMENTED()
 
 
 /*
@@ -105,31 +104,30 @@
 #define UNKNOWN_WC_ID ((apr_int64_t) -1)
 #define FORMAT_FROM_SDB (-1)
 
-
-/* Assert that the given PDH is usable.
-   NOTE: the expression is multiply-evaluated!!  */
-#define VERIFY_USABLE_PDH(pdh) SVN_ERR_ASSERT(  \
-    (pdh)->wcroot != NULL                       \
-    && (pdh)->wcroot->format == SVN_WC__VERSION)
-
-
-/* ### since we're putting the pristine files per-dir, then we don't need
-   ### to create subdirectories in order to keep the directory size down.
-   ### when we can aggregate pristine files across dirs/wcs, then we will
-   ### need to undo the SKIP. */
-#define SVN__SKIP_SUBDIR
-
-WC_QUERIES_SQL_DECLARE_STATEMENTS(statements);
-
-
 /* This is a character used to escape itself and the globbing character in
    globbing sql expressions below.  See escape_sqlite_like().
 
    NOTE: this should match the character used within wc-metadata.sql  */
 #define LIKE_ESCAPE_CHAR     "#"
 
+/* Calculates the depth of the relpath below "" */
+APR_INLINE static int relpath_op_depth(const char *relpath)
+{
+  int n = 1;
+  if (*relpath == '\0')
+    return 0;
 
-typedef struct {
+  do
+  {
+    if (*relpath == '/')
+      n++;
+  }
+  while (*(++relpath));
+
+  return n;
+}
+
+typedef struct insert_base_baton_t {
   /* common to all insertions into BASE */
   svn_wc__db_status_t status;
   svn_wc__db_kind_t kind;
@@ -144,6 +142,7 @@ typedef struct {
   svn_revnum_t changed_rev;
   apr_time_t changed_date;
   const char *changed_author;
+  apr_hash_t *dav_cache;
 
   /* for inserting directories */
   const apr_array_header_t *children;
@@ -166,11 +165,12 @@ typedef struct {
 
 
 typedef struct {
-  /* common to all insertions into WORKING */
+  /* common to all insertions into WORKING (including NODE_DATA) */
   svn_wc__db_status_t presence;
   svn_wc__db_kind_t kind;
   apr_int64_t wc_id;
   const char *local_relpath;
+  apr_int64_t op_depth;
 
   /* common to all "normal" presence insertions */
   const apr_hash_t *props;
@@ -202,7 +202,6 @@ static const svn_token_map_t kind_map[] 
   { "file", svn_wc__db_kind_file },
   { "dir", svn_wc__db_kind_dir },
   { "symlink", svn_wc__db_kind_symlink },
-  { "subdir", svn_wc__db_kind_subdir },
   { "unknown", svn_wc__db_kind_unknown },
   { NULL }
 };
@@ -281,15 +280,15 @@ escape_sqlite_like(const char * const st
    to hold CHECKSUM's pristine file, relating to the pristine store
    configured for the working copy indicated by PDH. The returned path
    does not necessarily currently exist.
-#ifndef SVN__SKIP_SUBDIR
+
    Iff CREATE_SUBDIR is TRUE, then this function will make sure that the
    parent directory of PRISTINE_ABSPATH exists. This is only useful when
    about to create a new pristine.
-#endif
+
    Any other allocations are made in SCRATCH_POOL. */
 static svn_error_t *
 get_pristine_fname(const char **pristine_abspath,
-                   svn_wc__db_pdh_t *pdh,
+                   const char *wcroot_abspath,
                    const svn_checksum_t *sha1_checksum,
                    svn_boolean_t create_subdir,
                    apr_pool_t *result_pool,
@@ -297,13 +296,11 @@ get_pristine_fname(const char **pristine
 {
   const char *base_dir_abspath;
   const char *hexdigest = svn_checksum_to_cstring(sha1_checksum, scratch_pool);
-#ifndef SVN__SKIP_SUBDIR
   char subdir[3];
-#endif
 
   /* ### code is in transition. make sure we have the proper data.  */
   SVN_ERR_ASSERT(pristine_abspath != NULL);
-  SVN_ERR_ASSERT(pdh->wcroot != NULL);
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(wcroot_abspath));
   SVN_ERR_ASSERT(sha1_checksum != NULL);
   SVN_ERR_ASSERT(sha1_checksum->kind == svn_checksum_sha1);
 
@@ -311,7 +308,7 @@ get_pristine_fname(const char **pristine
      ### to use join_many since we know "/" is the separator for
      ### internal canonical paths */
   base_dir_abspath = svn_dirent_join_many(scratch_pool,
-                                          pdh->wcroot->abspath,
+                                          wcroot_abspath,
                                           svn_wc_get_adm_dir(scratch_pool),
                                           PRISTINE_STORAGE_RELPATH,
                                           NULL);
@@ -319,7 +316,6 @@ get_pristine_fname(const char **pristine
   /* We should have a valid checksum and (thus) a valid digest. */
   SVN_ERR_ASSERT(hexdigest != NULL);
 
-#ifndef SVN__SKIP_SUBDIR
   /* Get the first two characters of the digest, for the subdir. */
   subdir[0] = hexdigest[0];
   subdir[1] = hexdigest[1];
@@ -339,14 +335,11 @@ get_pristine_fname(const char **pristine
          try to access the file within this (missing?) pristine subdir. */
       svn_error_clear(err);
     }
-#endif
 
   /* The file is located at DIR/.svn/pristine/XX/XXYYZZ... */
   *pristine_abspath = svn_dirent_join_many(result_pool,
                                            base_dir_abspath,
-#ifndef SVN__SKIP_SUBDIR
                                            subdir,
-#endif
                                            hexdigest,
                                            NULL);
   return SVN_NO_ERROR;
@@ -381,6 +374,94 @@ fetch_repos_info(const char **repos_root
   return svn_error_return(svn_sqlite__reset(stmt));
 }
 
+#ifdef SVN_WC__NODES
+
+/* Can't verify if we only have the NODES table */
+#ifndef SVN_WC__NODES_ONLY
+
+/* */
+static svn_error_t *
+assert_text_columns_equal(svn_sqlite__stmt_t *stmt1,
+                          svn_sqlite__stmt_t *stmt2,
+                          int column,
+                          apr_pool_t *scratch_pool)
+{
+  const char *val1 = svn_sqlite__column_text(stmt1, column, scratch_pool);
+  const char *val2 = svn_sqlite__column_text(stmt2, column, scratch_pool);
+
+  if (val1 != NULL && val2 != NULL) {
+    if (strcmp(val1, val2) != 0)
+      return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
+          "Value in statement 1 (%s) differs from value in statement 2 (%s)",
+                             val1, val2);
+  } else if (val1 == NULL && val2 == NULL) {
+    /* Do nothing: equal */
+  } else
+      return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
+          "Value in statement 1 (%s) differs from value in statement 2 (%s)",
+                             val1, val2);
+
+  return SVN_NO_ERROR;
+}
+
+#endif
+
+static svn_error_t *
+assert_base_rows_match(svn_boolean_t have_row1,
+                       svn_boolean_t have_row2,
+                       svn_sqlite__stmt_t *stmt1,
+                       svn_sqlite__stmt_t *stmt2,
+                       const char *relpath,
+                       apr_pool_t *scratch_pool)
+{
+
+  if (have_row1 != have_row2)
+    SVN_ERR(svn_error_createf(
+              SVN_ERR_WC_CORRUPT, NULL,
+              "Different results from BASE (%d) and NODES queries (%d), "
+              "for local_relpath %s",
+              have_row1, have_row2, relpath));
+
+  if (have_row1) {
+    SVN_ERR_ASSERT(svn_sqlite__column_int64(stmt1, 0)
+                   == svn_sqlite__column_int64(stmt2, 0));
+
+    SVN_ERR(assert_text_columns_equal(stmt1, stmt2, 1, scratch_pool));
+
+    SVN_ERR(assert_text_columns_equal(stmt1, stmt2, 2, scratch_pool));
+
+    SVN_ERR(assert_text_columns_equal(stmt1, stmt2, 3, scratch_pool));
+
+    SVN_ERR_ASSERT(svn_sqlite__column_revnum(stmt1, 4)
+                   == svn_sqlite__column_revnum(stmt2, 4));
+
+    SVN_ERR(assert_text_columns_equal(stmt1, stmt2, 5, scratch_pool));
+
+    SVN_ERR(assert_text_columns_equal(stmt1, stmt2, 6, scratch_pool));
+
+    SVN_ERR_ASSERT(svn_sqlite__column_revnum(stmt1, 7)
+                   == svn_sqlite__column_revnum(stmt2, 7));
+
+
+    SVN_ERR_ASSERT(svn_sqlite__column_int64(stmt1, 8)
+                   == svn_sqlite__column_int64(stmt2, 8));
+
+    SVN_ERR(assert_text_columns_equal(stmt1, stmt2, 9, scratch_pool));
+
+    SVN_ERR(assert_text_columns_equal(stmt1, stmt2, 10, scratch_pool));
+
+    SVN_ERR(assert_text_columns_equal(stmt1, stmt2, 11, scratch_pool));
+
+    SVN_ERR_ASSERT(svn_sqlite__column_int64(stmt1, 12)
+                   == svn_sqlite__column_int64(stmt2, 12));
+
+    /* 14: verify props? */
+  }
+
+  return SVN_NO_ERROR;
+}
+
+#endif
 
 /* Scan from LOCAL_RELPATH upwards through parent nodes until we find a parent
    that has values in the 'repos_id' and 'repos_relpath' columns.  Return
@@ -401,20 +482,52 @@ scan_upwards_for_repos(apr_int64_t *repo
   const char *current_relpath = local_relpath;
   svn_sqlite__stmt_t *stmt;
 
+#ifdef SVN_WC__NODES
+  svn_sqlite__stmt_t *data_stmt;
+#endif
+
   SVN_ERR_ASSERT(wcroot->sdb != NULL && wcroot->wc_id != UNKNOWN_WC_ID);
   SVN_ERR_ASSERT(repos_id != NULL || repos_relpath != NULL);
 
+#ifndef SVN_WC__NODES_ONLY
   /* ### is it faster to fetch fewer columns? */
   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                                     STMT_SELECT_BASE_NODE));
+#endif
+
+#ifdef SVN_WC__NODES
+  SVN_ERR(svn_sqlite__get_statement(&data_stmt, wcroot->sdb,
+                                    STMT_SELECT_BASE_NODE_1));
+#endif
 
   while (TRUE)
     {
       svn_boolean_t have_row;
+#ifdef SVN_WC__NODES
+      svn_boolean_t have_data_row;
+#endif
 
+#ifndef SVN_WC__NODES_ONLY
       /* Get the current node's repository information.  */
       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
       SVN_ERR(svn_sqlite__step(&have_row, stmt));
+#endif
+
+#ifdef SVN_WC__NODES
+
+      /* Get the current node's repository information.  */
+      SVN_ERR(svn_sqlite__bindf(data_stmt, "is",
+                                wcroot->wc_id, current_relpath));
+      SVN_ERR(svn_sqlite__step(&have_data_row, data_stmt));
+
+#ifndef SVN_WC__NODES_ONLY
+      /* When switching to NODES_ONLY, stop verifying our results. */
+      SVN_ERR(assert_base_rows_match(have_row, have_data_row,
+                                     stmt, data_stmt,
+                                     current_relpath,
+                                     scratch_pool));
+#endif
+#endif
 
       if (!have_row)
         {
@@ -437,6 +550,9 @@ scan_upwards_for_repos(apr_int64_t *repo
                 svn_dirent_local_style(local_abspath, scratch_pool));
             }
 
+#ifdef SVN_WC__NODES
+          SVN_ERR(svn_sqlite__reset(data_stmt));
+#endif
           return svn_error_compose_create(err, svn_sqlite__reset(stmt));
         }
 
@@ -456,10 +572,17 @@ scan_upwards_for_repos(apr_int64_t *repo
                                                                       NULL),
                                               relpath_suffix,
                                               result_pool);
+#ifdef SVN_WC__NODES
+          SVN_ERR(svn_sqlite__reset(data_stmt));
+#endif
           return svn_sqlite__reset(stmt);
         }
+#ifdef SVN_WC__NODES
+      SVN_ERR(svn_sqlite__reset(data_stmt));
+#endif
+#ifndef SVN_WC__NODES_ONLY
       SVN_ERR(svn_sqlite__reset(stmt));
-
+#endif
       if (*current_relpath == '\0')
         {
           /* We scanned all the way up, and did not find the information.
@@ -472,7 +595,7 @@ scan_upwards_for_repos(apr_int64_t *repo
 
       /* Strip a path segment off the end, and append it to the suffix
          that we'll use when we finally find a base relpath.  */
-      svn_relpath_split(current_relpath, &current_relpath, &current_basename,
+      svn_relpath_split(&current_relpath, &current_basename, current_relpath,
                         scratch_pool);
       relpath_suffix = svn_relpath_join(relpath_suffix, current_basename,
                                         scratch_pool);
@@ -516,54 +639,6 @@ get_statement_for_path(svn_sqlite__stmt_
 }
 
 
-/* */
-static svn_error_t *
-navigate_to_parent(svn_wc__db_pdh_t **parent_pdh,
-                   svn_wc__db_t *db,
-                   svn_wc__db_pdh_t *child_pdh,
-                   svn_sqlite__mode_t smode,
-                   apr_pool_t *scratch_pool)
-{
-  const char *parent_abspath;
-  const char *local_relpath;
-  svn_sqlite__stmt_t *stmt;
-  svn_boolean_t got_row;
-
-  if ((*parent_pdh = child_pdh->parent) != NULL
-      && (*parent_pdh)->wcroot != NULL)
-    return SVN_NO_ERROR;
-
-  /* Make sure we don't see the root as its own parent */
-  SVN_ERR_ASSERT(!svn_dirent_is_root(child_pdh->local_abspath,
-                                     strlen(child_pdh->local_abspath)));
-
-  parent_abspath = svn_dirent_dirname(child_pdh->local_abspath, scratch_pool);
-  SVN_ERR(svn_wc__db_pdh_parse_local_abspath(parent_pdh, &local_relpath, db,
-                              parent_abspath, smode,
-                              scratch_pool, scratch_pool));
-  VERIFY_USABLE_PDH(*parent_pdh);
-
-  /* Check that the parent has an entry for the child */
-  SVN_ERR(svn_sqlite__get_statement(&stmt, (*parent_pdh)->wcroot->sdb,
-                                    STMT_SELECT_SUBDIR));
-  SVN_ERR(svn_sqlite__bindf(stmt, "is", (*parent_pdh)->wcroot->wc_id,
-                            svn_dirent_basename(child_pdh->local_abspath,
-                                                NULL)));
-  SVN_ERR(svn_sqlite__step(&got_row, stmt));
-  SVN_ERR(svn_sqlite__reset(stmt));
-
-  if (!got_row)
-    return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
-                              _("'%s' does not have a parent."),
-                              svn_dirent_local_style(child_pdh->local_abspath,
-                                                     scratch_pool));
-
-  child_pdh->parent = *parent_pdh;
-
-  return SVN_NO_ERROR;
-}
-
-
 /* For a given REPOS_ROOT_URL/REPOS_UUID pair, return the existing REPOS_ID
    value. If one does not exist, then create a new one. */
 static svn_error_t *
@@ -623,29 +698,28 @@ insert_base_node(void *baton, svn_sqlite
 {
   const insert_base_baton_t *pibb = baton;
   svn_sqlite__stmt_t *stmt;
+#ifdef SVN_WC__NODES
+  svn_sqlite__stmt_t *stmt_node;
+#endif
+  /* The directory at the WCROOT has a NULL parent_relpath. Otherwise,
+     bind the appropriate parent_relpath. */
+  const char *parent_relpath =
+    (*pibb->local_relpath == '\0') ? NULL
+    : svn_relpath_dirname(pibb->local_relpath, scratch_pool);
 
   /* ### we can't handle this right now  */
   SVN_ERR_ASSERT(pibb->conflict == NULL);
 
+#ifndef SVN_WC__NODES_ONLY
   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_BASE_NODE));
-  SVN_ERR(svn_sqlite__bindf(stmt, "is", pibb->wc_id, pibb->local_relpath));
-
-  if (TRUE /* maybe_bind_repos() */)
-    {
-      SVN_ERR(svn_sqlite__bind_int64(stmt, 3, pibb->repos_id));
-      SVN_ERR(svn_sqlite__bind_text(stmt, 4, pibb->repos_relpath));
-    }
-
-  /* The directory at the WCROOT has a NULL parent_relpath. Otherwise,
-     bind the appropriate parent_relpath. */
-  if (*pibb->local_relpath != '\0')
-    SVN_ERR(svn_sqlite__bind_text(stmt, 5,
-                                  svn_relpath_dirname(pibb->local_relpath,
-                                                      scratch_pool)));
-
-  SVN_ERR(svn_sqlite__bind_token(stmt, 6, presence_map, pibb->status));
-  SVN_ERR(svn_sqlite__bind_token(stmt, 7, kind_map, pibb->kind));
-  SVN_ERR(svn_sqlite__bind_int64(stmt, 8, pibb->revision));
+  SVN_ERR(svn_sqlite__bindf(stmt, "isissttr",
+                            pibb->wc_id, pibb->local_relpath,
+                            pibb->repos_id,
+                            pibb->repos_relpath,
+                            parent_relpath,
+                            presence_map, pibb->status,
+                            kind_map, pibb->kind,
+                            pibb->revision));
 
   SVN_ERR(svn_sqlite__bind_properties(stmt, 9, pibb->props, scratch_pool));
 
@@ -674,20 +748,72 @@ insert_base_node(void *baton, svn_sqlite
         SVN_ERR(svn_sqlite__bind_text(stmt, 16, pibb->target));
     }
 
+  if (pibb->dav_cache)
+    SVN_ERR(svn_sqlite__bind_properties(stmt, 17, pibb->dav_cache,
+                                        scratch_pool));
+
   SVN_ERR(svn_sqlite__insert(NULL, stmt));
+#endif
+
+#ifdef SVN_WC__NODES
+  SVN_ERR(svn_sqlite__get_statement(&stmt_node, sdb, STMT_INSERT_NODE));
+  { svn_revnum_t rev = pibb->changed_rev;
+  SVN_ERR(svn_sqlite__bindf(stmt_node, "isisisr"
+                            "tstr"               /* 8 - 11 */
+                            "isnnnnns",          /* 12 - 19 */
+                            pibb->wc_id,         /* 1 */
+                            pibb->local_relpath, /* 2 */
+                            (apr_int64_t)0, /* op_depth is 0 for base */
+                            parent_relpath,      /* 4 */
+                            pibb->repos_id,
+                            pibb->repos_relpath,
+                            pibb->revision,
+                            presence_map, pibb->status, /* 8 */
+                            (pibb->kind == svn_wc__db_kind_dir) ? /* 9 */
+                               svn_depth_to_word(pibb->depth) : NULL,
+                            kind_map, pibb->kind, /* 10 */
+                            rev,                  /* 11 */
+                            pibb->changed_date,   /* 12 */
+                            pibb->changed_author, /* 13 */
+                            (pibb->kind == svn_wc__db_kind_symlink) ?
+                                pibb->target : NULL)); /* 19 */
+  }
+
+  if (pibb->kind == svn_wc__db_kind_file) {
+    SVN_ERR(svn_sqlite__bind_checksum(stmt_node, 14, pibb->checksum,
+                                      scratch_pool));
+    if (pibb->translated_size != SVN_INVALID_FILESIZE)
+      SVN_ERR(svn_sqlite__bind_int64(stmt_node, 16, pibb->translated_size));
+  }
+
+  SVN_ERR(svn_sqlite__bind_properties(stmt_node, 15, pibb->props,
+                                      scratch_pool));
+  if (pibb->dav_cache)
+    SVN_ERR(svn_sqlite__bind_properties(stmt_node, 18, pibb->dav_cache,
+                                        scratch_pool));
+
+  SVN_ERR(svn_sqlite__insert(NULL, stmt_node));
+#endif
 
   if (pibb->kind == svn_wc__db_kind_dir && pibb->children)
     {
       int i;
 
+#ifndef SVN_WC__NODES_ONLY
       SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
                                         STMT_INSERT_BASE_NODE_INCOMPLETE));
+#endif
+#ifdef SVN_WC__NODES
+      SVN_ERR(svn_sqlite__get_statement(&stmt_node, sdb,
+                                        STMT_INSERT_NODE));
+#endif
 
       for (i = pibb->children->nelts; i--; )
         {
           const char *name = APR_ARRAY_IDX(pibb->children, i, const char *);
 
-          SVN_ERR(svn_sqlite__bindf(stmt, "issi",
+#ifndef SVN_WC__NODES_ONLY
+          SVN_ERR(svn_sqlite__bindf(stmt, "issr",
                                     pibb->wc_id,
                                     svn_relpath_join(pibb->local_relpath,
                                                      name,
@@ -695,6 +821,20 @@ insert_base_node(void *baton, svn_sqlite
                                     pibb->local_relpath,
                                     (apr_int64_t)pibb->revision));
           SVN_ERR(svn_sqlite__insert(NULL, stmt));
+#endif
+#ifdef SVN_WC__NODES
+          SVN_ERR(svn_sqlite__bindf(stmt_node, "isisnnrsns",
+                                    pibb->wc_id,
+                                    svn_relpath_join(pibb->local_relpath,
+                                                     name,
+                                                     scratch_pool),
+                                    (apr_int64_t)0 /* BASE */,
+                                    pibb->local_relpath, /* parent_relpath */
+                                    pibb->revision,
+                                    "incomplete",
+                                    "unknown"));
+          SVN_ERR(svn_sqlite__insert(NULL, stmt_node));
+#endif
         }
     }
 
@@ -715,6 +855,43 @@ blank_iwb(insert_working_baton_t *piwb)
      value, but... meh. We'll avoid them if ORIGINAL_REPOS_RELPATH==NULL.  */
 }
 
+/* */
+static svn_error_t *
+copy_working_from_base(void *baton,
+                       svn_sqlite__db_t *sdb,
+                       apr_pool_t *scratch_pool)
+{
+  const insert_working_baton_t *piwb = baton;
+  svn_sqlite__stmt_t *stmt;
+
+#ifdef SVN_WC__NODES
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
+                         STMT_INSERT_WORKING_NODE_FROM_BASE));
+  SVN_ERR(svn_sqlite__bindf(stmt, "isit", piwb->wc_id,
+                            piwb->local_relpath,
+                            piwb->op_depth,
+                            presence_map, piwb->presence));
+  SVN_ERR(svn_sqlite__insert(NULL, stmt));
+
+#endif
+
+#ifndef SVN_WC__NODES_ONLY
+  /* Run the sequence below instead, which copies all the columns, including
+     those with the restrictions */
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
+                                    STMT_INSERT_WORKING_NODE_FROM_BASE_NODE));
+  SVN_ERR(svn_sqlite__bindf(stmt, "ist", piwb->wc_id, piwb->local_relpath,
+                            presence_map, piwb->presence));
+  SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
+
+  return SVN_NO_ERROR;
+}
+
+
+
 static svn_error_t *
 insert_incomplete_working_children(svn_sqlite__db_t *sdb,
                                    apr_int64_t wc_id,
@@ -722,22 +899,49 @@ insert_incomplete_working_children(svn_s
                                    const apr_array_header_t *children,
                                    apr_pool_t *scratch_pool)
 {
+#ifndef SVN_WC__NODES_ONLY
   svn_sqlite__stmt_t *stmt;
+#endif
+#ifdef SVN_WC__NODES
+  svn_sqlite__stmt_t *stmt_node;
+#endif
   int i;
 
+#ifndef SVN_WC__NODES_ONLY
   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
                                     STMT_INSERT_WORKING_NODE_INCOMPLETE));
+#endif
+#ifdef SVN_WC__NODES
+  SVN_ERR(svn_sqlite__get_statement(&stmt_node, sdb,
+                                    STMT_INSERT_NODE));
+#endif
+
 
   for (i = children->nelts; i--; )
     {
       const char *name = APR_ARRAY_IDX(children, i, const char *);
 
+#ifndef SVN_WC__NODES_ONLY
       SVN_ERR(svn_sqlite__bindf(stmt, "iss",
                                 wc_id,
                                 svn_relpath_join(local_relpath, name,
                                                  scratch_pool),
                                 local_relpath));
       SVN_ERR(svn_sqlite__insert(NULL, stmt));
+#endif
+#ifdef SVN_WC__NODES
+      SVN_ERR(svn_sqlite__bindf(stmt_node, "isisnnnsns",
+                                wc_id,
+                                svn_relpath_join(local_relpath, name,
+                                                 scratch_pool),
+                                (apr_int64_t) 2, /* ### op_depth
+                                                    non-THIS_DIR working */
+                                local_relpath,
+                                "incomplete", /* 8, presence */
+                                "unknown"));  /* 10, kind */
+
+      SVN_ERR(svn_sqlite__insert(NULL, stmt_node));
+#endif
     }
 
   return SVN_NO_ERROR;
@@ -752,17 +956,20 @@ insert_working_node(void *baton,
   const insert_working_baton_t *piwb = baton;
   const char *parent_relpath;
   svn_sqlite__stmt_t *stmt;
+#ifdef SVN_WC__NODES
+  svn_sqlite__stmt_t *stmt_node;
+  apr_int64_t op_depth;
+#endif
 
   /* We cannot insert a WORKING_NODE row at the wcroot.  */
   /* ### actually, with per-dir DB, we can... */
-#if 0
   SVN_ERR_ASSERT(*piwb->local_relpath != '\0');
-#endif
   if (*piwb->local_relpath == '\0')
     parent_relpath = NULL;
   else
     parent_relpath = svn_relpath_dirname(piwb->local_relpath, scratch_pool);
 
+#ifndef SVN_WC__NODES_ONLY
   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_WORKING_NODE));
   SVN_ERR(svn_sqlite__bindf(stmt, "isstt",
                             piwb->wc_id, piwb->local_relpath,
@@ -818,6 +1025,57 @@ insert_working_node(void *baton,
 
   SVN_ERR(svn_sqlite__insert(NULL, stmt));
 
+#endif
+
+#ifdef SVN_WC__NODES
+  op_depth = (parent_relpath == NULL) ? 1   /* THIS_DIR */
+                                      : 2;  /* immediate children */
+  SVN_ERR(svn_sqlite__get_statement(&stmt_node, sdb, STMT_INSERT_NODE));
+  SVN_ERR(svn_sqlite__bindf(stmt_node, "isisnnntstrisn"
+                "nnnn" /* properties translated_size last_mod_time dav_cache */
+                "s",
+                piwb->wc_id, piwb->local_relpath,
+                op_depth,
+                parent_relpath,
+                presence_map, piwb->presence,
+                (piwb->kind == svn_wc__db_kind_dir)
+                            ? svn_depth_to_word(piwb->depth) : NULL,
+                kind_map, piwb->kind,
+                piwb->changed_rev,
+                piwb->changed_date,
+                piwb->changed_author,
+                (piwb->kind == svn_wc__db_kind_symlink)
+                            ? piwb->target : NULL));
+
+
+  if (piwb->kind == svn_wc__db_kind_file)
+    {
+      SVN_ERR(svn_sqlite__bind_checksum(stmt_node, 14, piwb->checksum,
+                                        scratch_pool));
+    }
+  else if (piwb->kind == svn_wc__db_kind_symlink)
+    {
+      /* Note: incomplete nodes may have a NULL target.  */
+      if (piwb->target)
+        SVN_ERR(svn_sqlite__bind_text(stmt_node, 19, piwb->target));
+    }
+
+  if (piwb->original_repos_relpath != NULL)
+    {
+      SVN_ERR(svn_sqlite__bind_int64(stmt_node, 5, piwb->original_repos_id));
+      SVN_ERR(svn_sqlite__bind_text(stmt_node, 6,
+                    piwb->original_repos_relpath));
+      SVN_ERR(svn_sqlite__bind_int64(stmt_node, 7, piwb->original_revnum));
+    }
+
+
+  SVN_ERR(svn_sqlite__bind_properties(stmt_node, 15, piwb->props,
+                      scratch_pool));
+
+  SVN_ERR(svn_sqlite__insert(NULL, stmt_node));
+#endif
+
+
   if (piwb->kind == svn_wc__db_kind_dir && piwb->children)
     SVN_ERR(insert_incomplete_working_children(sdb, piwb->wc_id,
                                                piwb->local_relpath,
@@ -830,6 +1088,8 @@ insert_working_node(void *baton,
 }
 
 
+/* Return the number of children under PARENT_RELPATH in the given WC_ID.
+   The table is implicitly defined by the STMT_IDX query.  */
 static svn_error_t *
 count_children(int *count,
                int stmt_idx,
@@ -877,6 +1137,9 @@ add_children_to_hash(apr_hash_t *childre
 }
 
 
+/* When children of PARENT_RELPATH are in both BASE_NODE and WORKING_NODE,
+   this function can be used to union those two sets, returning the set
+   in *CHILDREN (allocated in RESULT_POOL).  */
 static svn_error_t *
 union_children(const apr_array_header_t **children,
                svn_sqlite__db_t *sdb,
@@ -902,6 +1165,13 @@ union_children(const apr_array_header_t 
 }
 
 
+/* Return all the children of PARENT_RELPATH from a single table, implicitly
+   defined by STMT_IDX. If the caller happens to know the count of children,
+   it should be passed as START_SIZE to pre-allocate space in the *CHILDREN
+   return value.
+
+   If the caller doesn't know the count, then it should pass a reasonable
+   idea of how many children may be present.  */
 static svn_error_t *
 single_table_children(const apr_array_header_t **children,
                       int stmt_idx,
@@ -939,7 +1209,10 @@ single_table_children(const apr_array_he
 }
 
 
-/* */
+/* Return in *CHILDREN all of the children of the directory LOCAL_ABSPATH.
+   If BASE_ONLY is true, then *only* the children from BASE_NODE are
+   returned (those in WORKING_NODE are ignored). The result children are
+   allocated in RESULT_POOl.  */
 static svn_error_t *
 gather_children(const apr_array_header_t **children,
                 svn_boolean_t base_only,
@@ -1010,11 +1283,30 @@ gather_children(const apr_array_header_t
 
 
 /* */
-static void
-flush_entries(const svn_wc__db_pdh_t *pdh)
+static svn_error_t *
+flush_entries(svn_wc__db_t *db,
+              svn_wc__db_pdh_t *pdh,
+              const char *local_abspath,
+              apr_pool_t *scratch_pool)
 {
   if (pdh->adm_access)
     svn_wc__adm_access_set_entries(pdh->adm_access, NULL);
+
+  if (local_abspath
+      && strcmp(local_abspath, pdh->local_abspath) == 0
+      && strcmp(local_abspath, pdh->wcroot->abspath) != 0)
+    {
+      svn_wc__db_pdh_t *parent_pdh;
+
+      SVN_ERR(svn_wc__db_pdh_navigate_to_parent(&parent_pdh, db, pdh,
+                                                svn_sqlite__mode_readonly,
+                                                scratch_pool));
+
+      if (parent_pdh->adm_access)
+        svn_wc__adm_access_set_entries(parent_pdh->adm_access, NULL);
+    }
+
+  return SVN_NO_ERROR;
 }
 
 
@@ -1122,6 +1414,7 @@ which_trees_exist(svn_boolean_t *base_ex
    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,
@@ -1153,6 +1446,7 @@ prop_upgrade_trees(svn_boolean_t *base_e
   else
     {
       *base_exists = TRUE;  /* value == 0  */
+      *base_presence = svn_sqlite__column_token(stmt, 1, presence_map);
     }
 
   /* Return the WC_ID that was assigned.  */
@@ -1169,6 +1463,8 @@ prop_upgrade_trees(svn_boolean_t *base_e
          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.  */
@@ -1200,6 +1496,11 @@ create_db(svn_sqlite__db_t **sdb,
   /* Create the database's schema.  */
   SVN_ERR(svn_sqlite__exec_statements(*sdb, STMT_CREATE_SCHEMA));
 
+#ifdef SVN_WC__NODES
+  /* Create the NODES table for the experimental schema */
+  SVN_ERR(svn_sqlite__exec_statements(*sdb, STMT_CREATE_NODES));
+#endif
+
   /* Insert the repository. */
   SVN_ERR(create_repos_id(repos_id, repos_root_url, repos_uuid, *sdb,
                           scratch_pool));
@@ -1325,55 +1626,135 @@ svn_wc__db_from_relpath(const char **loc
   return SVN_NO_ERROR;
 }
 
-
 svn_error_t *
-svn_wc__db_base_add_directory(svn_wc__db_t *db,
-                              const char *local_abspath,
-                              const char *repos_relpath,
-                              const char *repos_root_url,
-                              const char *repos_uuid,
-                              svn_revnum_t revision,
-                              const apr_hash_t *props,
-                              svn_revnum_t changed_rev,
-                              apr_time_t changed_date,
-                              const char *changed_author,
-                              const apr_array_header_t *children,
-                              svn_depth_t depth,
-                              const svn_skel_t *conflict,
-                              const svn_skel_t *work_items,
-                              apr_pool_t *scratch_pool)
+svn_wc__db_get_wcroot(const char **wcroot_abspath,
+                      svn_wc__db_t *db,
+                      const char *wri_abspath,
+                      apr_pool_t *result_pool,
+                      apr_pool_t *scratch_pool)
 {
   svn_wc__db_pdh_t *pdh;
-  const char *local_relpath;
-  apr_int64_t repos_id;
-  insert_base_baton_t ibb;
-
-  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
-  SVN_ERR_ASSERT(repos_relpath != NULL);
-  SVN_ERR_ASSERT(svn_uri_is_absolute(repos_root_url));
-  SVN_ERR_ASSERT(repos_uuid != NULL);
-  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
-  SVN_ERR_ASSERT(props != NULL);
-  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
-#if 0
-  SVN_ERR_ASSERT(children != NULL);
-#endif
+  const char *unused_relpath;
 
-  SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &local_relpath, db,
-                              local_abspath, svn_sqlite__mode_readwrite,
+  SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &unused_relpath, db,
+                              wri_abspath, svn_sqlite__mode_readonly,
                               scratch_pool, scratch_pool));
-  VERIFY_USABLE_PDH(pdh);
 
-  SVN_ERR(create_repos_id(&repos_id, repos_root_url, repos_uuid,
-                          pdh->wcroot->sdb, scratch_pool));
+  /* Can't use VERIFY_USABLE_PDH, as this should be usable to detect
+     where call upgrade */
 
-  blank_ibb(&ibb);
+  if (pdh->wcroot == NULL)
+    return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
+                             _("The node '%s' is not in a workingcopy."),
+                             svn_dirent_local_style(wri_abspath,
+                                                    scratch_pool));
 
-  ibb.status = svn_wc__db_status_normal;
-  ibb.kind = svn_wc__db_kind_dir;
-  ibb.wc_id = pdh->wcroot->wc_id;
-  ibb.local_relpath = local_relpath;
-  ibb.repos_id = repos_id;
+  *wcroot_abspath = apr_pstrdup(result_pool, pdh->wcroot->abspath);
+
+  return SVN_NO_ERROR;
+}
+
+struct with_sqlite_lock_baton
+{
+  svn_wc__db_t *db;
+  svn_wc__db_sqlite_lock_cb lock_cb;
+  void *lock_baton;
+};
+
+static svn_error_t *
+call_sqlite_lock_cb(void *baton,
+                    svn_sqlite__db_t *sdb,
+                    apr_pool_t *scratch_pool)
+{
+  struct with_sqlite_lock_baton *lb = baton;
+
+  return svn_error_return(lb->lock_cb(lb->db, lb->lock_baton, scratch_pool));
+}
+
+svn_error_t *
+svn_wc__db_with_sqlite_lock(svn_wc__db_t *db,
+                            const char *wri_abspath,
+                            svn_wc__db_sqlite_lock_cb lock_cb,
+                            void *cb_baton,
+                            apr_pool_t *scratch_pool)
+{
+  svn_wc__db_pdh_t *pdh;
+  const char *unused_relpath;
+  struct with_sqlite_lock_baton baton;
+
+  SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &unused_relpath, db,
+                              wri_abspath, svn_sqlite__mode_readonly,
+                              scratch_pool, scratch_pool));
+
+  /* Can't use VERIFY_USABLE_PDH, as this should be usable to detect
+     where call upgrade */
+
+  if (pdh->wcroot == NULL || !pdh->wcroot->sdb)
+    return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
+                             _("The node '%s' is not in a workingcopy."),
+                             svn_dirent_local_style(wri_abspath,
+                                                    scratch_pool));
+
+  baton.db = db;
+  baton.lock_cb = lock_cb;
+  baton.lock_baton = cb_baton;
+
+  return svn_error_return(
+            svn_sqlite__with_lock(pdh->wcroot->sdb,
+                                  call_sqlite_lock_cb,
+                                  &baton,
+                                  scratch_pool));
+}
+
+svn_error_t *
+svn_wc__db_base_add_directory(svn_wc__db_t *db,
+                              const char *local_abspath,
+                              const char *repos_relpath,
+                              const char *repos_root_url,
+                              const char *repos_uuid,
+                              svn_revnum_t revision,
+                              const apr_hash_t *props,
+                              svn_revnum_t changed_rev,
+                              apr_time_t changed_date,
+                              const char *changed_author,
+                              const apr_array_header_t *children,
+                              svn_depth_t depth,
+                              apr_hash_t *dav_cache,
+                              const svn_skel_t *conflict,
+                              const svn_skel_t *work_items,
+                              apr_pool_t *scratch_pool)
+{
+  svn_wc__db_pdh_t *pdh;
+  const char *local_relpath;
+  apr_int64_t repos_id;
+  insert_base_baton_t ibb;
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+  SVN_ERR_ASSERT(repos_relpath != NULL);
+  SVN_ERR_ASSERT(svn_uri_is_absolute(repos_root_url));
+  SVN_ERR_ASSERT(repos_uuid != NULL);
+  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
+  SVN_ERR_ASSERT(props != NULL);
+  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
+#if 0
+  SVN_ERR_ASSERT(children != NULL);
+#endif
+
+  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(create_repos_id(&repos_id, repos_root_url, repos_uuid,
+                          pdh->wcroot->sdb, scratch_pool));
+
+  blank_ibb(&ibb);
+
+  ibb.status = svn_wc__db_status_normal;
+  ibb.kind = svn_wc__db_kind_dir;
+  ibb.wc_id = pdh->wcroot->wc_id;
+  ibb.local_relpath = local_relpath;
+  ibb.repos_id = repos_id;
   ibb.repos_relpath = repos_relpath;
   ibb.revision = revision;
 
@@ -1385,6 +1766,7 @@ svn_wc__db_base_add_directory(svn_wc__db
   ibb.children = children;
   ibb.depth = depth;
 
+  ibb.dav_cache = dav_cache;
   ibb.conflict = conflict;
   ibb.work_items = work_items;
 
@@ -1397,7 +1779,7 @@ svn_wc__db_base_add_directory(svn_wc__db
                                        scratch_pool));
 
   /* ### worry about flushing child subdirs?  */
-  flush_entries(pdh);
+  SVN_ERR(flush_entries(db, pdh, local_abspath, scratch_pool));
   return SVN_NO_ERROR;
 }
 
@@ -1415,6 +1797,7 @@ svn_wc__db_base_add_file(svn_wc__db_t *d
                          const char *changed_author,
                          const svn_checksum_t *checksum,
                          svn_filesize_t translated_size,
+                         apr_hash_t *dav_cache,
                          const svn_skel_t *conflict,
                          const svn_skel_t *work_items,
                          apr_pool_t *scratch_pool)
@@ -1459,6 +1842,7 @@ svn_wc__db_base_add_file(svn_wc__db_t *d
   ibb.checksum = checksum;
   ibb.translated_size = translated_size;
 
+  ibb.dav_cache = dav_cache;
   ibb.conflict = conflict;
   ibb.work_items = work_items;
 
@@ -1470,7 +1854,7 @@ svn_wc__db_base_add_file(svn_wc__db_t *d
                                        insert_base_node, &ibb,
                                        scratch_pool));
 
-  flush_entries(pdh);
+  SVN_ERR(flush_entries(db, pdh, local_abspath, scratch_pool));
   return SVN_NO_ERROR;
 }
 
@@ -1487,6 +1871,7 @@ svn_wc__db_base_add_symlink(svn_wc__db_t
                             apr_time_t changed_date,
                             const char *changed_author,
                             const char *target,
+                            apr_hash_t *dav_cache,
                             const svn_skel_t *conflict,
                             const svn_skel_t *work_items,
                             apr_pool_t *scratch_pool)
@@ -1530,6 +1915,7 @@ svn_wc__db_base_add_symlink(svn_wc__db_t
 
   ibb.target = target;
 
+  ibb.dav_cache = dav_cache;
   ibb.conflict = conflict;
   ibb.work_items = work_items;
 
@@ -1541,7 +1927,7 @@ svn_wc__db_base_add_symlink(svn_wc__db_t
                                        insert_base_node, &ibb,
                                        scratch_pool));
 
-  flush_entries(pdh);
+  SVN_ERR(flush_entries(db, pdh, local_abspath, scratch_pool));
   return SVN_NO_ERROR;
 }
 
@@ -1609,67 +1995,9 @@ svn_wc__db_base_add_absent_node(svn_wc__
                                        insert_base_node, &ibb,
                                        scratch_pool));
 
-  flush_entries(pdh);
-  return SVN_NO_ERROR;
-}
-
-
-/* ### temp API.  Remove before release. */
-svn_error_t *
-svn_wc__db_temp_base_add_subdir(svn_wc__db_t *db,
-                                const char *local_abspath,
-                                const char *repos_relpath,
-                                const char *repos_root_url,
-                                const char *repos_uuid,
-                                svn_revnum_t revision,
-                                const apr_hash_t *props,
-                                svn_revnum_t changed_rev,
-                                apr_time_t changed_date,
-                                const char *changed_author,
-                                svn_depth_t depth,
-                                apr_pool_t *scratch_pool)
-{
-  svn_wc__db_pdh_t *pdh;
-  const char *local_relpath;
-  apr_int64_t repos_id;
-  insert_base_baton_t ibb;
-
-  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
-  SVN_ERR_ASSERT(repos_relpath != NULL);
-  SVN_ERR_ASSERT(svn_uri_is_absolute(repos_root_url));
-  SVN_ERR_ASSERT(repos_uuid != NULL);
-  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
-  SVN_ERR_ASSERT(props != NULL);
-  SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
-
-  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(create_repos_id(&repos_id, repos_root_url, repos_uuid,
-                          pdh->wcroot->sdb, scratch_pool));
-
-  ibb.status = svn_wc__db_status_normal;
-  ibb.kind = svn_wc__db_kind_subdir;
-  ibb.wc_id = pdh->wcroot->wc_id;
-  ibb.local_relpath = local_relpath;
-  ibb.repos_id = repos_id;
-  ibb.repos_relpath = repos_relpath;
-  ibb.revision = revision;
-
-  ibb.props = NULL;
-  ibb.changed_rev = changed_rev;
-  ibb.changed_date = changed_date;
-  ibb.changed_author = changed_author;
-
-  ibb.children = NULL;
-  ibb.depth = depth;
-
-  /* ### no children, conflicts, or work items to install in a txn... */
+  SVN_ERR(flush_entries(db, pdh, local_abspath, scratch_pool));
 
-  return svn_error_return(insert_base_node(&ibb, pdh->wcroot->sdb,
-                                           scratch_pool));
+  return SVN_NO_ERROR;
 }
 
 
@@ -1689,13 +2017,23 @@ svn_wc__db_base_remove(svn_wc__db_t *db,
                               scratch_pool, scratch_pool));
   VERIFY_USABLE_PDH(pdh);
 
+#ifndef SVN_WC__NODES_ONLY
   SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
                                     STMT_DELETE_BASE_NODE));
   SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath));
 
   SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
+
+#ifdef SVN_WC__NODES
+  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
+                                    STMT_DELETE_BASE_NODE_1));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath));
+
+  SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
 
-  flush_entries(pdh);
+  SVN_ERR(flush_entries(db, pdh, local_abspath, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -1724,8 +2062,14 @@ svn_wc__db_base_get_info(svn_wc__db_stat
 {
   svn_wc__db_pdh_t *pdh;
   const char *local_relpath;
+#ifndef SVN_WC__NODES_ONLY
   svn_sqlite__stmt_t *stmt;
   svn_boolean_t have_row;
+#endif
+#ifdef SVN_WC__NODES
+  svn_sqlite__stmt_t *stmt_nodes;
+  svn_boolean_t have_node_row;
+#endif
   svn_error_t *err = SVN_NO_ERROR;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
@@ -1735,11 +2079,25 @@ svn_wc__db_base_get_info(svn_wc__db_stat
                               scratch_pool, scratch_pool));
   VERIFY_USABLE_PDH(pdh);
 
+#ifndef SVN_WC__NODES_ONLY
   SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
                                     lock ? STMT_SELECT_BASE_NODE_WITH_LOCK
                                          : STMT_SELECT_BASE_NODE));
   SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath));
   SVN_ERR(svn_sqlite__step(&have_row, stmt));
+#endif
+
+#ifdef SVN_WC__NODES
+  SVN_ERR(svn_sqlite__get_statement(&stmt_nodes, pdh->wcroot->sdb,
+                                    lock ? STMT_SELECT_BASE_NODE_WITH_LOCK_1
+                                         : STMT_SELECT_BASE_NODE_1));
+  SVN_ERR(svn_sqlite__bindf(stmt_nodes, "is",
+                            pdh->wcroot->wc_id, local_relpath));
+  SVN_ERR(svn_sqlite__step(&have_node_row, stmt_nodes));
+
+  SVN_ERR(assert_base_rows_match(have_row, have_node_row, stmt, stmt_nodes,
+                                 local_relpath, scratch_pool));
+#endif
 
   if (have_row)
     {
@@ -1748,24 +2106,11 @@ svn_wc__db_base_get_info(svn_wc__db_stat
 
       if (kind)
         {
-          if (node_kind == svn_wc__db_kind_subdir)
-            *kind = svn_wc__db_kind_dir;
-          else
-            *kind = node_kind;
+          *kind = node_kind;
         }
       if (status)
         {
           *status = svn_sqlite__column_token(stmt, 2, presence_map);
-
-          if (node_kind == svn_wc__db_kind_subdir
-              && *status == svn_wc__db_status_normal)
-            {
-              /* We're looking at the subdir record in the *parent* directory,
-                 which implies per-dir .svn subdirs. We should be looking
-                 at the subdir itself; therefore, it is missing or obstructed
-                 in some way. Inform the caller.  */
-              *status = svn_wc__db_status_obstructed;
-            }
         }
       if (revision)
         {
@@ -1883,6 +2228,99 @@ svn_wc__db_base_get_info(svn_wc__db_stat
                                                      scratch_pool));
     }
 
+#ifdef SVN_WC__NODES
+  SVN_ERR(svn_sqlite__reset(stmt_nodes));
+#endif
+
+  /* Note: given the composition, no need to wrap for tracing.  */
+  return svn_error_compose_create(err, svn_sqlite__reset(stmt));
+}
+
+svn_error_t *
+svn_wc__db_base_get_info_from_parent(svn_wc__db_status_t *status,
+                                     svn_wc__db_kind_t *kind,
+                                     svn_revnum_t *revision,
+                                     const char **repos_relpath,
+                                     const char **repos_root_url,
+                                     const char **repos_uuid,
+                                     svn_wc__db_t *db,
+                                     const char *local_abspath,
+                                     apr_pool_t *result_pool,
+                                     apr_pool_t *scratch_pool)
+{
+  svn_wc__db_pdh_t *pdh;
+  const char *local_relpath;
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+  svn_error_t *err = SVN_NO_ERROR;
+  const char *parent_abspath;
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
+
+  parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
+
+  SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &local_relpath, db,
+                              parent_abspath, svn_sqlite__mode_readonly,
+                              scratch_pool, scratch_pool));
+  VERIFY_USABLE_PDH(pdh);
+
+  local_relpath = svn_relpath_join(local_relpath,
+                                   svn_dirent_basename(local_abspath, NULL),
+                                   scratch_pool);
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
+                                    STMT_SELECT_BASE_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)
+    {
+      svn_wc__db_kind_t node_kind = svn_sqlite__column_token(stmt, 3,
+                                                             kind_map);
+
+      if (kind)
+        {
+          *kind = node_kind;
+        }
+      if (status)
+        {
+          *status = svn_sqlite__column_token(stmt, 2, presence_map);
+        }
+      if (revision)
+        {
+          *revision = svn_sqlite__column_revnum(stmt, 4);
+        }
+      if (repos_relpath)
+        {
+          *repos_relpath = svn_sqlite__column_text(stmt, 1, result_pool);
+        }
+      if (repos_root_url || repos_uuid)
+        {
+          /* Fetch repository information via REPOS_ID. */
+          if (svn_sqlite__column_is_null(stmt, 0))
+            {
+              if (repos_root_url)
+                *repos_root_url = NULL;
+              if (repos_uuid)
+                *repos_uuid = NULL;
+            }
+          else
+            {
+              err = fetch_repos_info(repos_root_url, repos_uuid,
+                                     pdh->wcroot->sdb,
+                                     svn_sqlite__column_int64(stmt, 0),
+                                     result_pool);
+            }
+        }
+    }
+  else
+    {
+      err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
+                              _("The node '%s' was not found."),
+                              svn_dirent_local_style(local_abspath,
+                                                     scratch_pool));
+    }
+
   /* Note: given the composition, no need to wrap for tracing.  */
   return svn_error_compose_create(err, svn_sqlite__reset(stmt));
 }
@@ -1969,14 +2407,37 @@ svn_wc__db_base_set_dav_cache(svn_wc__db
                               apr_pool_t *scratch_pool)
 {
   svn_sqlite__stmt_t *stmt;
+  int affected_rows;
 
+#ifndef SVN_WC__NODES_ONLY
   SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
                                  STMT_UPDATE_BASE_DAV_CACHE, scratch_pool));
   SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
 
-  /* ### we should assert that 1 row was affected.  */
+  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
 
-  return svn_error_return(svn_sqlite__step_done(stmt));
+  if (affected_rows != 1)
+    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
+                             _("The node '%s' was not found."),
+                             svn_dirent_local_style(local_abspath,
+                                                    scratch_pool));
+#endif
+#ifdef SVN_WC__NODES
+  SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
+                                 STMT_UPDATE_BASE_NODE_DAV_CACHE,
+                                 scratch_pool));
+  SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
+
+  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
+
+  if (affected_rows != 1)
+    return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
+                             _("The node '%s' was not found."),
+                             svn_dirent_local_style(local_abspath,
+                                                    scratch_pool));
+#endif
+
+  return SVN_NO_ERROR;
 }
 
 
@@ -2007,8 +2468,50 @@ svn_wc__db_base_get_dav_cache(apr_hash_t
 
 
 svn_error_t *
-svn_wc__db_pristine_get_path(const char **pristine_abspath,
-                             svn_wc__db_t *db,
+svn_wc__db_base_clear_dav_cache_recursive(svn_wc__db_t *db,
+                                          const char *local_abspath,
+                                          apr_pool_t *scratch_pool)
+{
+  svn_wc__db_pdh_t *pdh;
+  const char *local_relpath;
+  const char *like_arg;
+  svn_sqlite__stmt_t *stmt;
+
+  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);
+
+  if (local_relpath[0] == 0)
+    like_arg = "%";
+  else
+    like_arg = apr_pstrcat(scratch_pool,
+                           escape_sqlite_like(local_relpath, scratch_pool),
+                           "/%", NULL);
+
+#ifndef SVN_WC__NODES_ONLY
+  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
+                                    STMT_CLEAR_BASE_RECURSIVE_DAV_CACHE));
+  SVN_ERR(svn_sqlite__bindf(stmt, "iss", pdh->wcroot->wc_id, local_relpath,
+                            like_arg));
+  SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
+#ifdef SVN_WC__NODES
+  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
+                                    STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE));
+  SVN_ERR(svn_sqlite__bindf(stmt, "iss", pdh->wcroot->wc_id, local_relpath,
+                            like_arg));
+
+  SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
+
+  return SVN_NO_ERROR;
+}
+
+svn_error_t *
+svn_wc__db_pristine_get_path(const char **pristine_abspath,
+                             svn_wc__db_t *db,
                              const char *wri_abspath,
                              const svn_checksum_t *sha1_checksum,
                              apr_pool_t *result_pool,
@@ -2016,6 +2519,7 @@ svn_wc__db_pristine_get_path(const char 
 {
   svn_wc__db_pdh_t *pdh;
   const char *local_relpath;
+  svn_boolean_t present;
 
   SVN_ERR_ASSERT(pristine_abspath != NULL);
   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
@@ -2034,15 +2538,33 @@ svn_wc__db_pristine_get_path(const char 
                                              scratch_pool, scratch_pool));
   VERIFY_USABLE_PDH(pdh);
 
-  /* ### should we look in the PRISTINE table for anything?  */
+  SVN_ERR(svn_wc__db_pristine_check(&present, db, wri_abspath, sha1_checksum,
+                                    scratch_pool));
+  if (! present)
+    return svn_error_createf(SVN_ERR_WC_DB_ERROR, NULL,
+                             _("Pristine text not found"));
 
-  SVN_ERR(get_pristine_fname(pristine_abspath, pdh, sha1_checksum,
+  SVN_ERR(get_pristine_fname(pristine_abspath, pdh->wcroot->abspath,
+                             sha1_checksum,
                              FALSE /* create_subdir */,
-                             scratch_pool, scratch_pool));
+                             result_pool, scratch_pool));
 
   return SVN_NO_ERROR;
 }
 
+svn_error_t *
+svn_wc__db_pristine_get_future_path(const char **pristine_abspath,
+                                    const char *wcroot_abspath,
+                                    svn_checksum_t *sha1_checksum,
+                                    apr_pool_t *result_pool,
+                                    apr_pool_t *scratch_pool)
+{
+  SVN_ERR(get_pristine_fname(pristine_abspath, wcroot_abspath,
+                             sha1_checksum,
+                             FALSE /* create_subdir */,
+                             result_pool, scratch_pool));
+  return SVN_NO_ERROR;
+}
 
 svn_error_t *
 svn_wc__db_pristine_read(svn_stream_t **contents,
@@ -2074,7 +2596,8 @@ svn_wc__db_pristine_read(svn_stream_t **
 
   /* ### should we look in the PRISTINE table for anything?  */
 
-  SVN_ERR(get_pristine_fname(&pristine_abspath, pdh, sha1_checksum,
+  SVN_ERR(get_pristine_fname(&pristine_abspath, pdh->wcroot->abspath,
+                             sha1_checksum,
                              FALSE /* create_subdir */,
                              scratch_pool, scratch_pool));
   return svn_error_return(svn_stream_open_readonly(
@@ -2123,6 +2646,7 @@ svn_wc__db_pristine_install(svn_wc__db_t
   const char *pristine_abspath;
   apr_finfo_t finfo;
   svn_sqlite__stmt_t *stmt;
+  svn_node_kind_t kind;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(tempfile_abspath));
   SVN_ERR_ASSERT(sha1_checksum != NULL);
@@ -2143,13 +2667,25 @@ svn_wc__db_pristine_install(svn_wc__db_t
                               scratch_pool, scratch_pool));
   VERIFY_USABLE_PDH(pdh);
 
-  SVN_ERR(get_pristine_fname(&pristine_abspath, pdh, sha1_checksum,
+  SVN_ERR(get_pristine_fname(&pristine_abspath, pdh->wcroot->abspath,
+                             sha1_checksum,
                              TRUE /* create_subdir */,
                              scratch_pool, scratch_pool));
 
+
+  SVN_ERR(svn_io_check_path(pristine_abspath, &kind, scratch_pool));
+
+  if (kind == svn_node_file)
+    {
+      /* Remove the tempfile, it's already there */
+      return svn_error_return(
+                  svn_io_remove_file2(tempfile_abspath,
+                                      FALSE, scratch_pool));
+    }
+
   /* Put the file into its target location.  */
-  SVN_ERR(svn_io_file_rename(tempfile_abspath, pristine_abspath,
-                             scratch_pool));
+    SVN_ERR(svn_io_file_rename(tempfile_abspath, pristine_abspath,
+                               scratch_pool));
 
   SVN_ERR(svn_io_stat(&finfo, pristine_abspath, APR_FINFO_SIZE,
                       scratch_pool));
@@ -2245,6 +2781,32 @@ svn_wc__db_pristine_get_sha1(const svn_c
 }
 
 
+/* Delete the pristine text referenced by SHA1_CHECKSUM in the database
+ * referenced by PDH. */
+static svn_error_t *
+pristine_remove(svn_wc__db_pdh_t *pdh,
+                const svn_checksum_t *sha1_checksum,
+                apr_pool_t *scratch_pool)
+{
+  svn_sqlite__stmt_t *stmt;
+  const char *pristine_abspath;
+
+  /* Remove the DB row. */
+  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
+                                    STMT_DELETE_PRISTINE));
+  SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
+  SVN_ERR(svn_sqlite__update(NULL, stmt));
+
+  /* Remove the file */
+  SVN_ERR(get_pristine_fname(&pristine_abspath, pdh->wcroot->abspath,
+                             sha1_checksum, TRUE /* create_subdir */,
+                             scratch_pool, scratch_pool));
+  SVN_ERR(svn_io_remove_file2(pristine_abspath, TRUE /* ignore_enoent */,
+                              scratch_pool));
+
+  return SVN_NO_ERROR;
+}
+
 svn_error_t *
 svn_wc__db_pristine_remove(svn_wc__db_t *db,
                            const char *wri_abspath,
@@ -2270,6 +2832,21 @@ svn_wc__db_pristine_remove(svn_wc__db_t 
                               scratch_pool, scratch_pool));
   VERIFY_USABLE_PDH(pdh);
 
+  /* If the work queue is not empty, don't delete any pristine text because
+   * the work queue may contain a reference to it. */
+  {
+    svn_sqlite__stmt_t *stmt;
+    svn_boolean_t have_row;
+
+    SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
+                                      STMT_LOOK_FOR_WORK));
+    SVN_ERR(svn_sqlite__step(&have_row, stmt));
+    SVN_ERR(svn_sqlite__reset(stmt));
+
+    if (have_row)
+      return SVN_NO_ERROR;
+  }
+
   /* Find whether the SHA-1 (or the MD-5) is referenced; set IS_REFERENCED. */
   {
     const svn_checksum_t *md5_checksum;
@@ -2289,24 +2866,50 @@ svn_wc__db_pristine_remove(svn_wc__db_t 
     SVN_ERR(svn_sqlite__reset(stmt));
   }
 
-  /* If not referenced, remove first the PRISTINE table row, then the file. */
+  /* If not referenced, remove the PRISTINE table row and the file. */
   if (! is_referenced)
     {
-      svn_sqlite__stmt_t *stmt;
-      const char *pristine_abspath;
+      SVN_ERR(pristine_remove(pdh, sha1_checksum, scratch_pool));
+    }
 
-      /* Remove the DB row. */
-      SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
-                                        STMT_DELETE_PRISTINE));
-      SVN_ERR(svn_sqlite__bind_checksum(stmt, 1, sha1_checksum, scratch_pool));
-      SVN_ERR(svn_sqlite__update(NULL, stmt));
-
-      /* Remove the file */
-      SVN_ERR(get_pristine_fname(&pristine_abspath, pdh, sha1_checksum,
-                                 TRUE /* create_subdir */,
-                                 scratch_pool, scratch_pool));
-      SVN_ERR(svn_io_remove_file2(pristine_abspath, TRUE, scratch_pool));
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_wc__db_pristine_cleanup(svn_wc__db_t *db,
+                            const char *wri_abspath,
+                            apr_pool_t *scratch_pool)
+{
+  svn_wc__db_pdh_t *pdh;
+  const char *local_relpath;
+  svn_sqlite__stmt_t *stmt;
+
+  SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
+
+  SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &local_relpath, db,
+                              wri_abspath, svn_sqlite__mode_readonly,
+                              scratch_pool, scratch_pool));
+  VERIFY_USABLE_PDH(pdh);
+
+  /* Find the pristines in the DB */
+  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
+                                    STMT_SELECT_PRISTINE_ROWS));
+  while (1)
+    {
+      svn_boolean_t have_row;
+      const svn_checksum_t *checksum;
+
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+      if (! have_row)
+        break;
+
+      SVN_ERR(svn_sqlite__column_checksum(&checksum, stmt, 0,
+                                          scratch_pool));
+      SVN_ERR(svn_wc__db_pristine_remove(db, wri_abspath, checksum,
+                                         scratch_pool));
     }
+  SVN_ERR(svn_sqlite__reset(stmt));
 
   return SVN_NO_ERROR;
 }
@@ -2317,7 +2920,6 @@ svn_wc__db_pristine_check(svn_boolean_t 
                           svn_wc__db_t *db,
                           const char *wri_abspath,
                           const svn_checksum_t *sha1_checksum,
-                          svn_wc__db_checkmode_t mode,
                           apr_pool_t *scratch_pool)
 {
   svn_wc__db_pdh_t *pdh;
@@ -2351,7 +2953,8 @@ svn_wc__db_pristine_check(svn_boolean_t 
   SVN_ERR(svn_sqlite__reset(stmt));
 
   /* Check that the pristine text file exists. */
-  SVN_ERR(get_pristine_fname(&pristine_abspath, pdh, sha1_checksum,
+  SVN_ERR(get_pristine_fname(&pristine_abspath, pdh->wcroot->abspath,
+                             sha1_checksum,
                              FALSE /* create_subdir */,
                              scratch_pool, scratch_pool));
   SVN_ERR(svn_io_check_path(pristine_abspath, &kind_on_disk, scratch_pool));
@@ -2432,7 +3035,7 @@ temp_cross_db_copy(svn_wc__db_t *db,
 
   SVN_ERR_ASSERT(kind == svn_wc__db_kind_file
                  || kind == svn_wc__db_kind_dir
-                 || kind == svn_wc__db_kind_subdir);
+                 );
 
   SVN_ERR(svn_wc__db_read_info(NULL /* status */,
                                NULL /* kind */,
@@ -2572,7 +3175,7 @@ get_info_for_copy(apr_int64_t *copyfrom_
       svn_wc__db_kind_t parent_kind;
       svn_boolean_t parent_have_work;
 
-      svn_dirent_split(local_abspath, &parent_abspath, &base_name,
+      svn_dirent_split(&parent_abspath, &base_name, local_abspath,
                        scratch_pool);
       SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev,
                                 &parent_status, &parent_kind, &parent_have_work,
@@ -2582,15 +3185,7 @@ get_info_for_copy(apr_int64_t *copyfrom_
         *copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, base_name,
                                              result_pool);
     }
-  else if (*status != svn_wc__db_status_added)
-    {
-      *copyfrom_relpath = repos_relpath;
-      *copyfrom_rev = revision;
-      SVN_ERR(create_repos_id(copyfrom_id,
-                              repos_root_url, repos_uuid,
-                              pdh->wcroot->sdb, scratch_pool));
-    }
-  else
+  else if (*status == svn_wc__db_status_added)
     {
       const char *op_root_abspath;
       const char *original_repos_relpath, *original_root_url, *original_uuid;
@@ -2626,6 +3221,66 @@ get_info_for_copy(apr_int64_t *copyfrom_
           *copyfrom_id = 0;
         }
     }
+  else if (*status == svn_wc__db_status_deleted)
+    {
+      const char *work_del_abspath;
+
+      SVN_ERR(svn_wc__db_scan_deletion(NULL, NULL, NULL,
+                                       &work_del_abspath,
+                                       db, local_abspath,
+                                       scratch_pool, scratch_pool));
+      if (work_del_abspath)
+        {
+          const char *op_root_abspath;
+          const char *original_repos_relpath, *original_root_url;
+          const char *original_uuid;
+          svn_revnum_t original_revision;
+          const char *parent_del_abspath = svn_dirent_dirname(work_del_abspath,
+                                                              scratch_pool);
+
+          /* Similar to, but not the same as, the _scan_addition and
+             _join above.  Can we use get_copyfrom here? */
+          SVN_ERR(svn_wc__db_scan_addition(NULL, &op_root_abspath,
+                                           NULL /* repos_relpath */,
+                                           NULL /* repos_root_url */,
+                                           NULL /* repos_uuid */,
+                                           &original_repos_relpath,
+                                           &original_root_url, &original_uuid,
+                                           &original_revision,
+                                           db, parent_del_abspath,
+                                           scratch_pool, scratch_pool));
+          *copyfrom_relpath
+            = svn_relpath_join(original_repos_relpath,
+                               svn_dirent_skip_ancestor(op_root_abspath,
+                                                        local_abspath),
+                               scratch_pool);
+          *copyfrom_rev = original_revision;
+          SVN_ERR(create_repos_id(copyfrom_id,
+                                  original_root_url, original_uuid,
+                                  pdh->wcroot->sdb, scratch_pool));
+        }
+      else
+        {
+          *copyfrom_relpath = repos_relpath;
+          *copyfrom_rev = revision;
+          if (!repos_root_url || !repos_uuid)
+            SVN_ERR(svn_wc__db_scan_base_repos(NULL,
+                                               &repos_root_url, &repos_uuid,
+                                               db, local_abspath,
+                                               scratch_pool, scratch_pool));
+          SVN_ERR(create_repos_id(copyfrom_id,
+                                  repos_root_url, repos_uuid,
+                                  pdh->wcroot->sdb, scratch_pool));
+        }
+    }
+  else
+    {
+      *copyfrom_relpath = repos_relpath;
+      *copyfrom_rev = revision;
+      SVN_ERR(create_repos_id(copyfrom_id,
+                              repos_root_url, repos_uuid,
+                              pdh->wcroot->sdb, scratch_pool));
+    }
 
   return SVN_NO_ERROR;
 }
@@ -2700,24 +3355,6 @@ svn_wc__db_op_copy(svn_wc__db_t *db,
                                                       scratch_pool));
     }
 
-
-  /* When copying a directory the destination may not exist, if so we
-     only copy the parent stub */
-  if (kind == svn_wc__db_kind_dir && !*src_relpath && *dst_relpath)
-    {
-      /* ### copy_tests.py 69 copies from the root of one wc to
-         ### another wc, that means the source doesn't have a
-         ### versioned parent and so there is no parent stub to
-         ### copy. We could generate a parent stub but it becomes
-         ### unnecessary when we centralise so for the moment we just
-         ### fail. */
-      SVN_ERR(navigate_to_parent(&src_pdh, db, src_pdh,
-                                 svn_sqlite__mode_readwrite, scratch_pool));
-      src_relpath = svn_dirent_basename(src_abspath, NULL);
-      kind = svn_wc__db_kind_subdir;
-    }
-
-  /* Get the children for a directory if this is not the parent stub */
   if (kind == svn_wc__db_kind_dir)
     SVN_ERR(gather_children(&children, FALSE, db, src_abspath,
                             scratch_pool, scratch_pool));
@@ -2730,6 +3367,7 @@ svn_wc__db_op_copy(svn_wc__db_t *db,
       const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
                                                            scratch_pool);
 
+#ifndef SVN_WC__NODES_ONLY
       if (have_work)
         SVN_ERR(svn_sqlite__get_statement(&stmt, src_pdh->wcroot->sdb,
                                   STMT_INSERT_WORKING_NODE_COPY_FROM_WORKING));
@@ -2749,6 +3387,34 @@ svn_wc__db_op_copy(svn_wc__db_t *db,
           SVN_ERR(svn_sqlite__bind_int64(stmt, 8, copyfrom_rev));
         }
       SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
+
+#ifdef SVN_WC__NODES
+
+      if (have_work)
+        SVN_ERR(svn_sqlite__get_statement(&stmt, src_pdh->wcroot->sdb,
+                         STMT_INSERT_WORKING_NODE_COPY_FROM_WORKING_1));
+      else
+        SVN_ERR(svn_sqlite__get_statement(&stmt, src_pdh->wcroot->sdb,
+                          STMT_INSERT_WORKING_NODE_COPY_FROM_BASE_1));
+
+      SVN_ERR(svn_sqlite__bindf(stmt, "issisnnnt",
+                    src_pdh->wcroot->wc_id, src_relpath,
+                    dst_relpath,
+                    (children == NULL) ? (apr_int64_t)2 :
+                                (apr_int64_t)1, /* no directory or stub */
+                    dst_parent_relpath,
+                    presence_map, dst_status));
+
+      if (copyfrom_relpath)
+        {
+          SVN_ERR(svn_sqlite__bind_int64(stmt, 6, copyfrom_id));
+          SVN_ERR(svn_sqlite__bind_text(stmt, 7, copyfrom_relpath));
+          SVN_ERR(svn_sqlite__bind_int64(stmt, 8, copyfrom_rev));
+        }
+      SVN_ERR(svn_sqlite__step_done(stmt));
+
+#endif
 
       /* ### Copying changelist is OK for a move but what about a copy? */
       SVN_ERR(svn_sqlite__get_statement(&stmt, src_pdh->wcroot->sdb,
@@ -2848,33 +3514,7 @@ svn_wc__db_op_copy_dir(svn_wc__db_t *db,
   SVN_ERR(svn_sqlite__with_transaction(pdh->wcroot->sdb,
                                        insert_working_node, &iwb,
                                        scratch_pool));
-  flush_entries(pdh);
-
-  /* Add a parent stub.  */
-  if (*local_relpath == '\0')
-    {
-      svn_error_t *err;
-
-      err = navigate_to_parent(&pdh, db, pdh, svn_sqlite__mode_readwrite,
-                               scratch_pool);
-      if (err)
-        {
-          /* Prolly fell off the top of the wcroot. Just call it a day.  */
-          svn_error_clear(err);
-          return SVN_NO_ERROR;
-        }
-
-      blank_iwb(&iwb);
-
-      iwb.presence = svn_wc__db_status_normal;
-      iwb.kind = svn_wc__db_kind_subdir;
-      iwb.wc_id = pdh->wcroot->wc_id;
-      iwb.local_relpath = svn_dirent_basename(local_abspath, scratch_pool);
-
-      /* No children or work items, so a txn is not needed.  */
-      SVN_ERR(insert_working_node(&iwb, pdh->wcroot->sdb, scratch_pool));
-      flush_entries(pdh);
-    }
+  SVN_ERR(flush_entries(db, pdh, local_abspath, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -2945,7 +3585,7 @@ svn_wc__db_op_copy_file(svn_wc__db_t *db
   SVN_ERR(svn_sqlite__with_transaction(pdh->wcroot->sdb,
                                        insert_working_node, &iwb,
                                        scratch_pool));
-  flush_entries(pdh);
+  SVN_ERR(flush_entries(db, pdh, local_abspath, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -3012,7 +3652,7 @@ svn_wc__db_op_copy_symlink(svn_wc__db_t 
   SVN_ERR(svn_sqlite__with_transaction(pdh->wcroot->sdb,
                                        insert_working_node, &iwb,
                                        scratch_pool));
-  flush_entries(pdh);
+  SVN_ERR(flush_entries(db, pdh, local_abspath, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -3047,33 +3687,7 @@ svn_wc__db_op_add_directory(svn_wc__db_t
   SVN_ERR(svn_sqlite__with_transaction(pdh->wcroot->sdb,
                                        insert_working_node, &iwb,
                                        scratch_pool));
-  flush_entries(pdh);
-
-  /* Add a parent stub.  */
-  if (*local_relpath == '\0')
-    {
-      svn_error_t *err;
-
-      err = navigate_to_parent(&pdh, db, pdh, svn_sqlite__mode_readwrite,
-                               scratch_pool);
-      if (err)
-        {
-          /* Prolly fell off the top of the wcroot. Just call it a day.  */
-          svn_error_clear(err);
-          return SVN_NO_ERROR;
-        }
-
-      blank_iwb(&iwb);
-
-      iwb.presence = svn_wc__db_status_normal;
-      iwb.kind = svn_wc__db_kind_subdir;
-      iwb.wc_id = pdh->wcroot->wc_id;
-      iwb.local_relpath = svn_dirent_basename(local_abspath, scratch_pool);
-
-      /* No children or work items, so a txn is not needed.  */
-      SVN_ERR(insert_working_node(&iwb, pdh->wcroot->sdb, scratch_pool));
-      flush_entries(pdh);
-    }
+  SVN_ERR(flush_entries(db, pdh, local_abspath, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -3108,7 +3722,7 @@ svn_wc__db_op_add_file(svn_wc__db_t *db,
   SVN_ERR(svn_sqlite__with_transaction(pdh->wcroot->sdb,
                                        insert_working_node, &iwb,
                                        scratch_pool));
-  flush_entries(pdh);
+  SVN_ERR(flush_entries(db, pdh, local_abspath, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -3147,7 +3761,7 @@ svn_wc__db_op_add_symlink(svn_wc__db_t *
   SVN_ERR(svn_sqlite__with_transaction(pdh->wcroot->sdb,
                                        insert_working_node, &iwb,
                                        scratch_pool));
-  flush_entries(pdh);
+  SVN_ERR(flush_entries(db, pdh, local_abspath, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -3188,7 +3802,7 @@ set_props_txn(void *baton, svn_sqlite__d
   SVN_ERR(svn_sqlite__bind_properties(stmt, 3, spb->props, scratch_pool));
   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
 
-  if (affected_rows == 1)
+  if (affected_rows == 1 || !spb->props)
     return SVN_NO_ERROR; /* We are done */
 
   /* We have to insert a row in ACTUAL */
@@ -3233,6 +3847,7 @@ svn_wc__db_op_set_props(svn_wc__db_t *db
                                          scratch_pool));
 }
 
+#ifdef SVN__SUPPORT_BASE_MERGE
 
 /* Set properties in a given table. The row must exist.  */
 static svn_error_t *
@@ -3271,10 +3886,20 @@ svn_wc__db_temp_base_set_props(svn_wc__d
                                const apr_hash_t *props,
                                apr_pool_t *scratch_pool)
 {
+#ifdef SVN_WC__NODES
+  SVN_ERR(set_properties(db, local_abspath, props,
+                         STMT_UPDATE_NODE_BASE_PROPS,
+                         "base node", scratch_pool));
+#endif
+
+#ifndef SVN_WC__NODES_ONLY
   return svn_error_return(set_properties(db, local_abspath, props,
                                          STMT_UPDATE_BASE_PROPS,
                                          "base_node",
                                          scratch_pool));
+#else
+  return SVN_NO_ERROR;
+#endif
 }
 
 
@@ -3284,12 +3909,23 @@ svn_wc__db_temp_working_set_props(svn_wc
                                   const apr_hash_t *props,
                                   apr_pool_t *scratch_pool)
 {
+#ifdef SVN_WC__NODES
+  SVN_ERR(set_properties(db, local_abspath, props,
+                         STMT_UPDATE_NODE_WORKING_PROPS,
+                         "working node", scratch_pool));
+#endif
+
+#ifndef SVN_WC__NODES_ONLY
   return svn_error_return(set_properties(db, local_abspath, props,
                                          STMT_UPDATE_WORKING_PROPS,
                                          "working_node",
                                          scratch_pool));
+#else
+  return SVN_NO_ERROR;
+#endif
 }
 
+#endif /* SVN__SUPPORT_BASE_MERGE  */
 
 svn_error_t *
 svn_wc__db_op_delete(svn_wc__db_t *db,
@@ -3409,7 +4045,9 @@ svn_wc__db_op_set_changelist(svn_wc__db_
   SVN_ERR(svn_sqlite__with_transaction(pdh->wcroot->sdb, set_changelist_txn,
                                        &scb, scratch_pool));
 
-  flush_entries(pdh);
+  /* No need to flush the parent entries; changelists were not stored in the
+     stub */
+  SVN_ERR(flush_entries(db, pdh, NULL, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -3471,7 +4109,7 @@ svn_wc__db_op_mark_resolved(svn_wc__db_t
     }
 
   /* Some entries have cached the above values. Kapow!!  */
-  flush_entries(pdh);
+  SVN_ERR(flush_entries(db, pdh, local_abspath, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -3574,7 +4212,7 @@ svn_wc__db_op_set_tree_conflict(svn_wc__
                                        scratch_pool));
 
   /* There may be some entries, and the lock info is now out of date.  */
-  flush_entries(pdh);
+  SVN_ERR(flush_entries(db, pdh, local_abspath, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -3699,11 +4337,12 @@ svn_wc__db_temp_op_remove_entry(svn_wc__
                               scratch_pool, scratch_pool));
   VERIFY_USABLE_PDH(pdh);
 
-  flush_entries(pdh);
+  SVN_ERR(flush_entries(db, pdh, local_abspath, scratch_pool));
 
   sdb = pdh->wcroot->sdb;
   wc_id = pdh->wcroot->wc_id;
 
+#ifndef SVN_WC__NODES_ONLY
   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_DELETE_BASE_NODE));
   SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
   SVN_ERR(svn_sqlite__step_done(stmt));
@@ -3711,39 +4350,18 @@ svn_wc__db_temp_op_remove_entry(svn_wc__
   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_DELETE_WORKING_NODE));
   SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
   SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
 
-  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_DELETE_ACTUAL_NODE));
+#ifdef SVN_WC__NODES
+  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_DELETE_NODES));
   SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
-
   SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
 
-  /* Check if we should also remove it from the parent db */
-  if (*local_relpath == '\0')
-    {
-      SVN_ERR(navigate_to_parent(&pdh, db, pdh, svn_sqlite__mode_readwrite,
-                                 scratch_pool));
-      VERIFY_USABLE_PDH(pdh);
-
-      local_relpath = svn_dirent_basename(local_abspath, NULL);
-
-      flush_entries(pdh);
-
-      sdb = pdh->wcroot->sdb;
-      wc_id = pdh->wcroot->wc_id;
-
-      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_DELETE_BASE_NODE));
-      SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
-      SVN_ERR(svn_sqlite__step_done(stmt));
-
-      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_DELETE_WORKING_NODE));
-      SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
-      SVN_ERR(svn_sqlite__step_done(stmt));
-
-      SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_DELETE_ACTUAL_NODE));
-      SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
+  SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_DELETE_ACTUAL_NODE));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
 
-      SVN_ERR(svn_sqlite__step_done(stmt));
-    }
+  SVN_ERR(svn_sqlite__step_done(stmt));
 
   return SVN_NO_ERROR;
 }
@@ -3765,49 +4383,41 @@ svn_wc__db_temp_op_remove_working(svn_wc
                               scratch_pool, scratch_pool));
   VERIFY_USABLE_PDH(pdh);
 
-  flush_entries(pdh);
+  SVN_ERR(flush_entries(db, pdh, local_abspath, scratch_pool));
 
+#ifndef SVN_WC__NODES_ONLY
   SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
                                     STMT_DELETE_WORKING_NODE));
   SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath));
   SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
 
-  /* Check if we should remove it from the parent db as well. */
-  if (*local_relpath == '\0')
-    {
-      SVN_ERR(navigate_to_parent(&pdh, db, pdh, svn_sqlite__mode_readwrite,
-                                 scratch_pool));
-      VERIFY_USABLE_PDH(pdh);
-
-      local_relpath = svn_dirent_basename(local_abspath, NULL);
-
-      flush_entries(pdh);
-
-      SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
-                                        STMT_DELETE_WORKING_NODE));
-      SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath));
-      SVN_ERR(svn_sqlite__step_done(stmt));
-    }
+#ifdef SVN_WC__NODES
+  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
+                                    STMT_DELETE_WORKING_NODES));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath));
+  SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
 
   return SVN_NO_ERROR;
 }
 
 
 static svn_error_t *
-update_depth_values(svn_wc__db_pdh_t *pdh,
+update_depth_values(svn_wc__db_t *db,
+                    const char *local_abspath,
+                    svn_wc__db_pdh_t *pdh,
                     const char *local_relpath,
-                    svn_depth_t depth)
+                    svn_depth_t depth,
+                    apr_pool_t *scratch_pool)
 {
   svn_boolean_t excluded = (depth == svn_depth_exclude);
   svn_sqlite__stmt_t *stmt;
 
   /* Flush any entries before we start monkeying the database.  */
-  flush_entries(pdh);
-
-  /* Parent stubs have only two depth options: excluded, or infinity.  */
-  if (*local_relpath != '\0' && !excluded)
-    depth = svn_depth_infinity;
+  SVN_ERR(flush_entries(db, pdh, local_abspath, scratch_pool));
 
+#ifndef SVN_WC__NODES_ONLY
   SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
                                     excluded
                                       ? STMT_UPDATE_BASE_EXCLUDED
@@ -3816,7 +4426,20 @@ update_depth_values(svn_wc__db_pdh_t *pd
   if (!excluded)
     SVN_ERR(svn_sqlite__bind_text(stmt, 3, svn_depth_to_word(depth)));
   SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
+
+#ifdef SVN_WC__NODES
+  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
+                                    excluded
+                                      ? STMT_UPDATE_NODE_BASE_EXCLUDED
+                                      : STMT_UPDATE_NODE_BASE_DEPTH));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath));
+  if (!excluded)
+    SVN_ERR(svn_sqlite__bind_text(stmt, 3, svn_depth_to_word(depth)));
+  SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
 
+#ifndef SVN_WC__NODES_ONLY
   SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
                                     excluded
                                       ? STMT_UPDATE_WORKING_EXCLUDED
@@ -3825,14 +4448,26 @@ update_depth_values(svn_wc__db_pdh_t *pd
   if (!excluded)
     SVN_ERR(svn_sqlite__bind_text(stmt, 3, svn_depth_to_word(depth)));
   SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
 
-  return SVN_NO_ERROR;
-}
-
-
-svn_error_t *
-svn_wc__db_temp_op_set_dir_depth(svn_wc__db_t *db,
-                                 const char *local_abspath,
+#ifdef SVN_WC__NODES
+  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
+                                    excluded
+                                      ? STMT_UPDATE_NODE_WORKING_EXCLUDED
+                                      : STMT_UPDATE_NODE_WORKING_DEPTH));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath));
+  if (!excluded)
+    SVN_ERR(svn_sqlite__bind_text(stmt, 3, svn_depth_to_word(depth)));
+  SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
+
+  return SVN_NO_ERROR;
+}
+
+
+svn_error_t *
+svn_wc__db_temp_op_set_dir_depth(svn_wc__db_t *db,
+                                 const char *local_abspath,
                                  svn_depth_t depth,
                                   apr_pool_t *scratch_pool)
 {
@@ -3850,30 +4485,8 @@ svn_wc__db_temp_op_set_dir_depth(svn_wc_
   /* ### We set depth on working and base to match entry behavior.
          Maybe these should be separated later? */
 
-  SVN_ERR(update_depth_values(pdh, local_relpath, depth));
-
-  /* If we're in the subdir, then navigate to the parent to set its
-     depth value.  */
-  if (*local_relpath == '\0')
-    {
-      svn_error_t *err;
-
-      err = navigate_to_parent(&pdh, db, pdh, svn_sqlite__mode_readwrite,
-                               scratch_pool);
-      if (err)
-        {
-          if (! SVN_WC__ERR_IS_NOT_CURRENT_WC(err))
-            return svn_error_return(err);
-
-          /* No parent to update */
-          svn_error_clear(err);
-          return SVN_NO_ERROR;
-        }
-
-      /* Get the stub name, and update the depth.  */
-      local_relpath = svn_dirent_basename(local_abspath, scratch_pool);
-      SVN_ERR(update_depth_values(pdh, local_relpath, depth));
-    }
+  SVN_ERR(update_depth_values(db, local_abspath, pdh, local_relpath, depth,
+                              scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -3896,17 +4509,21 @@ db_working_update_presence(svn_wc__db_st
                               scratch_pool, scratch_pool));
   VERIFY_USABLE_PDH(pdh);
 
+#ifndef SVN_WC__NODES_ONLY
   SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
                                     STMT_UPDATE_WORKING_PRESENCE));
   SVN_ERR(svn_sqlite__bindf(stmt, "ist", pdh->wcroot->wc_id, local_relpath,
                             presence_map, status));
   SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
 
-  flush_entries(pdh);
-
-  /* ### Parent stub?  I don't know; I'll punt for now as it passes
-         the regression tests as is and the problem will evaporate
-         when the db is centralised. */
+#ifdef SVN_WC__NODES
+  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
+                                    STMT_UPDATE_NODE_WORKING_PRESENCE));
+  SVN_ERR(svn_sqlite__bindf(stmt, "ist", pdh->wcroot->wc_id, local_relpath,
+                            presence_map, status));
+  SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
 
   return SVN_NO_ERROR;
 }
@@ -3929,38 +4546,33 @@ db_working_actual_remove(svn_wc__db_t *d
 
   VERIFY_USABLE_PDH(pdh);
 
+#ifndef SVN_WC__NODES_ONLY
   SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
                                     STMT_DELETE_WORKING_NODE));
   SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath));
   SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
+
+#ifdef SVN_WC__NODES
   SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
-                                    STMT_DELETE_ACTUAL_NODE));
+                                    STMT_DELETE_WORKING_NODES));
   SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath));
   SVN_ERR(svn_sqlite__step_done(stmt));
+#endif
 
-  flush_entries(pdh);
-
-  if (*local_relpath == '\0')
-    {
-      /* ### Delete parent stub. Remove when db is centralised. */
-      SVN_ERR(navigate_to_parent(&pdh, db, pdh, svn_sqlite__mode_readwrite,
-                                 scratch_pool));
-      local_relpath = svn_dirent_basename(local_abspath, NULL);
-      VERIFY_USABLE_PDH(pdh);
-
-      SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
-                                        STMT_DELETE_WORKING_NODE));
-      SVN_ERR(svn_sqlite__bindf(stmt, "is",
-                                pdh->wcroot->wc_id, local_relpath));
-      SVN_ERR(svn_sqlite__step_done(stmt));
+  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
+                                    STMT_DELETE_ACTUAL_NODE));
+  SVN_ERR(svn_sqlite__bindf(stmt, "is", pdh->wcroot->wc_id, local_relpath));
+  SVN_ERR(svn_sqlite__step_done(stmt));
 
-      flush_entries(pdh);
-    }
+  SVN_ERR(flush_entries(db, pdh, local_abspath, scratch_pool));
 
   return SVN_NO_ERROR;
 }
 
 
+
+
 /* Insert a working node for LOCAL_ABSPATH with presence=STATUS. */
 static svn_error_t *
 db_working_insert(svn_wc__db_status_t status,
@@ -3970,7 +4582,7 @@ db_working_insert(svn_wc__db_status_t st
 {
   svn_wc__db_pdh_t *pdh;
   const char *local_relpath;
-  svn_sqlite__stmt_t *stmt;
+  insert_working_baton_t iwb;
 
   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
   SVN_ERR(svn_wc__db_pdh_parse_local_abspath(&pdh, &local_relpath, db,
@@ -3978,33 +4590,21 @@ db_working_insert(svn_wc__db_status_t st
                               scratch_pool, scratch_pool));
   VERIFY_USABLE_PDH(pdh);
 
-  SVN_ERR(svn_sqlite__get_statement(&stmt, pdh->wcroot->sdb,
-                                    STMT_INSERT_WORKING_NODE_FROM_BASE_NODE));
-  SVN_ERR(svn_sqlite__bindf(stmt, "ist", pdh->wcroot->wc_id, local_relpath,
-                            presence_map, status));
-  SVN_ERR(svn_sqlite__step_done(stmt));
+  /* Update WORKING_NODE and NODE_DATA transactionally */
+  blank_iwb(&iwb);
 
-  flush_entries(pdh);
+  iwb.wc_id = pdh->wcroot->wc_id;
+  iwb.local_relpath = local_relpath;
+  iwb.presence = status;
+  /* ### NODE_DATA we temporary store 1 or 2 */
+  iwb.op_depth = (*local_relpath == '\0') ? 1 : 2;
 
-  if (*local_relpath == '\0')
-    {
-      /* ### Insert parent stub. Remove when db is centralised. */
-      SVN_ERR(navigate_to_parent(&pdh, db, pdh, svn_sqlite__mode_readwrite,
-                                 scratch_pool));
-      local_relpath = svn_dirent_basename(local_abspath, NULL);
-      VERIFY_USABLE_PDH(pdh);
+  SVN_ERR(svn_sqlite__with_transaction(pdh->wcroot->sdb,
+                                       copy_working_from_base, &iwb,
+                                       scratch_pool));
 
-      /* ### Should the parent stub have a full row like this? */
-      SVN_ERR(svn_sqlite__get_statement(
-                &stmt, pdh->wcroot->sdb,
-                STMT_INSERT_WORKING_NODE_FROM_BASE_NODE));
-      SVN_ERR(svn_sqlite__bindf(stmt, "ist",
-                                pdh->wcroot->wc_id, local_relpath,
-                                presence_map, status));
-      SVN_ERR(svn_sqlite__step_done(stmt));
 
-      flush_entries(pdh);
-    }
+  SVN_ERR(flush_entries(db, pdh, local_abspath, scratch_pool));
 
   return SVN_NO_ERROR;
 }
@@ -4055,7 +4655,7 @@ is_add_or_root_of_copy(svn_boolean_t *ad
       svn_revnum_t parent_original_revision;
       svn_error_t *err;
 
-      svn_dirent_split(local_abspath, &parent_abspath, &name, scratch_pool);
+      svn_dirent_split(&parent_abspath, &name, local_abspath, scratch_pool);
 
       err = svn_wc__db_scan_addition(&parent_status,
                                      NULL, NULL, NULL, NULL,
@@ -4128,8 +4728,7 @@ svn_wc__db_temp_op_delete(svn_wc__db_t *
                                NULL, NULL, NULL, &have_work, NULL, NULL,
                                db, local_abspath,
                                scratch_pool, scratch_pool));
-  if (working_status == svn_wc__db_status_deleted
-      || working_status == svn_wc__db_status_obstructed_delete)
+  if (working_status == svn_wc__db_status_deleted)
     {
       /* The node is already deleted.  */

[... 2515 lines stripped ...]