Author: rhuijben
Date: Mon Feb 23 17:05:31 2015
New Revision: 1661718

URL: http://svn.apache.org/r1661718
Log:
Fix the move state corruption problem found during the recent wc-db
work, where an incoming delete was turned into a copy, but some moves
that can't be represented after making a copy were only half broken.

* subversion/libsvn_wc/wc-queries.sql
  (STMT_SELECT_MOVED_DESCENDANTS_SHD,
   STMT_SELECT_MOVED_DESCENDANTS_SRC): Only scan the node itself for moves.

* subversion/libsvn_wc/wc_db.c
  (db_move_moved_to): New function. Similar to the old make_copy_move_moved_to.
  (db_move_moved_to_down_recursive): New function.
     Extracted from svn_wc__db_op_make_copy_internal.
  (make_copy_txn): Introduce not present nodes after recursing, to avoid making
     the move information invalid. Move moves to the original shadowing layer
     if we can't represent them in WORKING. Update caller.
  (svn_wc__db_op_make_copy_internal): Update caller.

* subversion/tests/libsvn_wc/op-depth-test.c
  (verify_db_callback,
   verify_db): New function.
  (make_copy_mixed,
   make_copy_and_delete_mixed): Call verify_db to fail on
     invalid db state.
  (test_list): Remove XFail marker from make_copy_and_delete_mixed.

Modified:
    subversion/trunk/subversion/libsvn_wc/wc-queries.sql
    subversion/trunk/subversion/libsvn_wc/wc_db.c
    subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c

Modified: subversion/trunk/subversion/libsvn_wc/wc-queries.sql
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc-queries.sql?rev=1661718&r1=1661717&r2=1661718&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc-queries.sql (original)
+++ subversion/trunk/subversion/libsvn_wc/wc-queries.sql Mon Feb 23 17:05:31 
2015
@@ -1690,7 +1690,7 @@ JOIN nodes n ON n.wc_id = ?1 AND n.local
                  WHERE d.wc_id = ?1 AND d.local_relpath = ?2
                    AND d.op_depth < ?3)
 WHERE s.wc_id = ?1 AND s.op_depth = ?3
-  AND IS_STRICT_DESCENDANT_OF(s.local_relpath, ?2)
+  AND (s.local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(s.local_relpath, ?2))
   AND s.moved_to IS NOT NULL
 
 /* This statement is very similar to STMT_SELECT_MOVED_DESCENDANTS_SHD,
@@ -1705,7 +1705,7 @@ JOIN nodes s ON s.wc_id = n.wc_id AND s.
                       AND d.local_relpath = s.local_relpath
                       AND d.op_depth > ?3)
 WHERE n.wc_id = ?1 AND n.op_depth = ?3
-  AND IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2)
+  AND (n.local_relpath = ?2 OR IS_STRICT_DESCENDANT_OF(n.local_relpath, ?2))
   AND s.moved_to IS NOT NULL
 
 -- STMT_COMMIT_UPDATE_ORIGIN

Modified: subversion/trunk/subversion/libsvn_wc/wc_db.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/libsvn_wc/wc_db.c?rev=1661718&r1=1661717&r2=1661718&view=diff
==============================================================================
--- subversion/trunk/subversion/libsvn_wc/wc_db.c (original)
+++ subversion/trunk/subversion/libsvn_wc/wc_db.c Mon Feb 23 17:05:31 2015
@@ -14793,6 +14793,87 @@ svn_wc__db_temp_op_start_directory_updat
   return SVN_NO_ERROR;
 }
 
+/* Helper for svn_wc__db_op_make_copy_internal */
+static svn_error_t *
+db_move_moved_to(svn_wc__db_wcroot_t *wcroot,
+                 const char *src1_relpath,
+                 int src1_op_depth,
+                 const char *src2_relpath,
+                 int src2_op_depth,
+                 const char *dst_relpath,
+                 apr_pool_t *scratch_pool)
+{
+  svn_sqlite__stmt_t *stmt;
+  int affected_rows;
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                     STMT_UPDATE_MOVED_TO_RELPATH));
+  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
+                            src1_relpath, src1_op_depth));
+  SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
+
+  if (affected_rows == 1)
+    {
+      SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                                     STMT_UPDATE_MOVED_TO_RELPATH));
+      SVN_ERR(svn_sqlite__bindf(stmt, "isds", wcroot->wc_id,
+                                src2_relpath, src2_op_depth,
+                                dst_relpath));
+      SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
+    }
+  if (affected_rows != 1)
+    return svn_error_create(SVN_ERR_WC_PATH_NOT_FOUND, NULL, NULL);
+
+  return SVN_NO_ERROR;
+}
+
+static svn_error_t *
+db_move_moved_to_down_recursive(svn_wc__db_wcroot_t *wcroot,
+                                const char *local_relpath,
+                                int new_shadow_layer,
+                                apr_pool_t *scratch_pool)
+{
+  svn_sqlite__stmt_t *stmt;
+  svn_boolean_t have_row;
+  apr_pool_t *iterpool = svn_pool_create(scratch_pool);
+
+  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
+                        STMT_SELECT_MOVED_DESCENDANTS_SRC));
+  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
+                            new_shadow_layer));
+  SVN_ERR(svn_sqlite__step(&have_row, stmt));
+
+  while (have_row)
+    {
+      int del_op_depth;
+      const char *src_relpath;
+      const char *dst_relpath;
+      svn_error_t *err;
+
+      svn_pool_clear(iterpool);
+
+      del_op_depth = svn_sqlite__column_int(stmt, 0);
+      src_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
+      dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool);
+
+      err = svn_error_trace(
+               db_move_moved_to(
+                             wcroot,
+                             src_relpath, del_op_depth,
+                             src_relpath, new_shadow_layer,
+                             dst_relpath, iterpool));
+
+      if (err)
+        return svn_error_compose_create(err, svn_sqlite__reset(stmt));
+
+      SVN_ERR(svn_sqlite__step(&have_row, stmt));
+    }
+
+  SVN_ERR(svn_sqlite__reset(stmt));
+
+  return SVN_NO_ERROR;
+}
+
 
 /* The body of svn_wc__db_temp_op_make_copy().  This is
    used by the update editor when deleting a base node tree would be a
@@ -14835,6 +14916,7 @@ make_copy_txn(svn_wc__db_wcroot_t *wcroo
               svn_revnum_t last_revision,
               int last_op_depth,
               svn_boolean_t shadowed,
+              int root_shadow_depth,
               apr_pool_t *scratch_pool)
 {
   svn_sqlite__stmt_t *stmt;
@@ -14875,24 +14957,6 @@ make_copy_txn(svn_wc__db_wcroot_t *wcroo
         op_depth = last_op_depth;
     }
 
-  /* Insert a not-present node to mark that we don't know what exists
-     here */
-  if (last_op_depth > 0 && last_op_depth != op_depth)
-    {
-      insert_working_baton_t iwb;
-
-      blank_iwb(&iwb);
-      iwb.presence = svn_wc__db_status_not_present;
-      iwb.op_depth = last_op_depth;
-
-      iwb.original_repos_id = repos_id;
-      iwb.original_repos_relpath = repos_relpath;
-      iwb.original_revnum = revision;
-      iwb.kind = kind;
-
-      SVN_ERR(insert_working_node(&iwb, wcroot, local_relpath, scratch_pool));
-    }
-
   /* Can we add a new copy node at the wanted op-depth? */
   if (!have_row || op_depth == last_op_depth)
     {
@@ -14929,7 +14993,8 @@ make_copy_txn(svn_wc__db_wcroot_t *wcroo
 
               SVN_ERR(make_copy_txn(wcroot, copy_relpath,
                                     repos_id, repos_relpath, revision,
-                                    op_depth, shadowed, scratch_pool));
+                                    op_depth, shadowed, root_shadow_depth,
+                                    scratch_pool));
             }
           svn_pool_destroy(iterpool);
         }
@@ -14945,6 +15010,9 @@ make_copy_txn(svn_wc__db_wcroot_t *wcroo
          any.
        */
       /* BASE_DELETED may be at op_depth, so let's use last_op_depth! */
+      SVN_ERR(db_move_moved_to_down_recursive(wcroot, local_relpath,
+                                              root_shadow_depth,
+                                              scratch_pool));
 
       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
                     STMT_DELETE_WORKING_BASE_DELETE));
@@ -14958,31 +15026,25 @@ make_copy_txn(svn_wc__db_wcroot_t *wcroo
       SVN_ERR(svn_sqlite__step_done(stmt));
     }
 
-  return SVN_NO_ERROR;
-}
+  /* Insert a not-present node to mark that we don't know what exists here.
 
-/* Helper for svn_wc__db_op_make_copy_internal */
-static svn_error_t *
-make_copy_move_moved_to(svn_wc__db_wcroot_t *wcroot,
-                        const char *local_relpath,
-                        int cur_op_depth,
-                        int new_op_depth,
-                        const char *moved_to,
-                        apr_pool_t *scratch_pool)
-{
-  svn_sqlite__stmt_t *stmt;
+     We do this last (after recursing), to allow the move fix-up code to
+     see the original moves. */
+  if (last_op_depth > 0 && last_op_depth != op_depth)
+    {
+      insert_working_baton_t iwb;
 
-  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                    STMT_UPDATE_MOVED_TO_RELPATH));
-  SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
-                            local_relpath, cur_op_depth));
-  SVN_ERR(svn_sqlite__step_done(stmt));
+      blank_iwb(&iwb);
+      iwb.presence = svn_wc__db_status_not_present;
+      iwb.op_depth = last_op_depth;
 
-  SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
-                                    STMT_UPDATE_MOVED_TO_RELPATH));
-  SVN_ERR(svn_sqlite__bindf(stmt, "isds", wcroot->wc_id,
-                            local_relpath, new_op_depth, moved_to));
-  SVN_ERR(svn_sqlite__step_done(stmt));
+      iwb.original_repos_id = repos_id;
+      iwb.original_repos_relpath = repos_relpath;
+      iwb.original_revnum = revision;
+      iwb.kind = kind;
+
+      SVN_ERR(insert_working_node(&iwb, wcroot, local_relpath, scratch_pool));
+    }
 
   return SVN_NO_ERROR;
 }
@@ -15030,14 +15092,13 @@ svn_wc__db_op_make_copy_internal(svn_wc_
     }
   else
     {
-      apr_pool_t *iterpool = svn_pool_create(scratch_pool);
-      svn_error_t *err = NULL;
       int affected_rows;
 
       op_depth = relpath_depth(local_relpath);
       /* We don't allow copies to contain server-excluded nodes;
          the update editor is going to have to bail out. */
-      SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath, iterpool));
+      SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath,
+                                            scratch_pool));
 
       /* Insert a shadowing layer */
       SVN_ERR(svn_sqlite__get_statement(
@@ -15053,38 +15114,14 @@ svn_wc__db_op_make_copy_internal(svn_wc_
       SVN_ERR_ASSERT(affected_rows > 0);
 
       if (!move_move_info)
-        {
-          SVN_ERR(svn_sqlite__get_statement(
-                            &stmt, wcroot->sdb,
-                            STMT_SELECT_MOVED_DESCENDANTS_SRC));
-          SVN_ERR(svn_sqlite__bindf(stmt, "isd",
-                                    wcroot->wc_id, local_relpath,
-                                    op_depth));
-
-          SVN_ERR(svn_sqlite__step(&have_row, stmt));
-
-          while (have_row && !err)
-            {
-              err = make_copy_move_moved_to(
-                                  wcroot,
-                                  svn_sqlite__column_text(stmt, 1, iterpool),
-                                  svn_sqlite__column_int(stmt, 0),
-                                  op_depth,
-                                  svn_sqlite__column_text(stmt, 4, iterpool),
-                                  iterpool);
-
-              SVN_ERR(svn_sqlite__step(&have_row, stmt));
-            }
-
-          SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
-        }
-
-      
+        SVN_ERR(db_move_moved_to_down_recursive(wcroot, local_relpath,
+                                                op_depth, scratch_pool));
 
 
       SVN_ERR(make_copy_txn(wcroot, local_relpath,
                             INVALID_REPOS_ID, NULL, SVN_INVALID_REVNUM,
-                            relpath_depth(local_relpath), FALSE, 
scratch_pool));
+                            op_depth, FALSE, op_depth,
+                            scratch_pool));
     }
 
   if (conflicts)

Modified: subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c
URL: 
http://svn.apache.org/viewvc/subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c?rev=1661718&r1=1661717&r2=1661718&view=diff
==============================================================================
--- subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c (original)
+++ subversion/trunk/subversion/tests/libsvn_wc/op-depth-test.c Mon Feb 23 
17:05:31 2015
@@ -562,6 +562,34 @@ check_db_conflicts(svn_test__sandbox_t *
   return comparison_baton.errors;
 }
 
+static svn_error_t *
+verify_db_callback(void *baton,
+                   const char *wc_abspath,
+                   const char *local_relpath,
+                   int op_depth,
+                   int id,
+                   const char *msg,
+                   apr_pool_t *scratch_pool)
+{
+  if (op_depth >= 0)
+    return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
+                "Verify: %s: %s (%d): SV%04d %s",
+                wc_abspath, local_relpath, op_depth, id, msg);
+  else
+    return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
+                "DB-VRFY: %s: %s: SV%04d %s",
+                wc_abspath, local_relpath, id, msg);
+}
+
+static svn_error_t *
+verify_db(svn_test__sandbox_t *b)
+{
+  SVN_ERR(svn_wc__db_verify_db_full(b->wc_ctx->db, b->wc_abspath,
+                                    verify_db_callback, NULL, b->pool));
+
+  return SVN_NO_ERROR;
+}
+
 
 /* ---------------------------------------------------------------------- */
 /* The test functions */
@@ -11410,6 +11438,8 @@ make_copy_mixed(const svn_test_opts_t *o
     SVN_ERR(check_db_rows(&b, "", nodes));
   }
 
+  SVN_ERR(verify_db(&b));
+
   return SVN_NO_ERROR;
 }
 
@@ -11560,13 +11590,15 @@ make_copy_and_delete_mixed(const svn_tes
     SVN_ERR(check_db_rows(&b, "", nodes));
   }
 
+  SVN_ERR(verify_db(&b));
+
   return SVN_NO_ERROR;
 }
 
 /* ---------------------------------------------------------------------- */
 /* The list of test functions */
 
-static int max_threads = 2;
+static int max_threads = 4;
 
 static struct svn_test_descriptor_t test_funcs[] =
   {
@@ -11774,7 +11806,7 @@ static struct svn_test_descriptor_t test
                        "move deep bump"),
     SVN_TEST_OPTS_PASS(make_copy_mixed,
                        "make a copy of a mixed revision tree"),
-    SVN_TEST_OPTS_XFAIL(make_copy_and_delete_mixed,
+    SVN_TEST_OPTS_PASS(make_copy_and_delete_mixed,
                        "make a copy of a mixed revision tree and del"),
     SVN_TEST_NULL
   };


Reply via email to