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
};