2 * wc_db.c : manipulating the administrative database
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
24 #define SVN_WC__I_AM_WC_DB
27 #include <apr_pools.h>
30 #include "svn_types.h"
31 #include "svn_error.h"
32 #include "svn_dirent_uri.h"
35 #include "svn_sorts.h"
37 #include "svn_checksum.h"
38 #include "svn_pools.h"
42 #include "adm_files.h"
43 #include "wc-queries.h"
46 #include "conflicts.h"
47 #include "wc_db_private.h"
48 #include "workqueue.h"
49 #include "token-map.h"
51 #include "svn_private_config.h"
52 #include "private/svn_sqlite.h"
53 #include "private/svn_skel.h"
54 #include "private/svn_wc_private.h"
55 #include "private/svn_token.h"
58 #define NOT_IMPLEMENTED() SVN__NOT_IMPLEMENTED()
62 * Some filename constants.
64 #define SDB_FILE "wc.db"
66 #define WCROOT_TEMPDIR_RELPATH "tmp"
70 * PARAMETER ASSERTIONS
72 * Every (semi-)public entrypoint in this file has a set of assertions on
73 * the parameters passed into the function. Since this is a brand new API,
74 * we want to make sure that everybody calls it properly. The original WC
75 * code had years to catch stray bugs, but we do not have that luxury in
76 * the wc-nb rewrite. Any extra assurances that we can find will be
77 * welcome. The asserts will ensure we have no doubt about the values
78 * passed into the function.
80 * Some parameters are *not* specifically asserted. Typically, these are
81 * params that will be used immediately, so something like a NULL value
84 * ### near 1.7 release, it would be a Good Thing to review the assertions
85 * ### and decide if any can be removed or switched to assert() in order
86 * ### to remove their runtime cost in the production release.
91 * Each function should leave the database in a consistent state. If it
92 * does *not*, then the implication is some other function needs to be
93 * called to restore consistency. Subtle requirements like that are hard
94 * to maintain over a long period of time, so this API will not allow it.
97 * STANDARD VARIABLE NAMES
99 * db working copy database (this module)
100 * sdb SQLite database (not to be confused with 'db')
101 * wc_id a WCROOT id associated with a node
104 #define INVALID_REPOS_ID ((apr_int64_t) -1)
105 #define UNKNOWN_WC_ID ((apr_int64_t) -1)
106 #define FORMAT_FROM_SDB (-1)
108 /* Check if column number I, a property-skel column, contains a non-empty
109 set of properties. The empty set of properties is stored as "()", so we
110 have properties if the size of the column is larger than 2. */
111 #define SQLITE_PROPERTIES_AVAILABLE(stmt, i) \
112 (svn_sqlite__column_bytes(stmt, i) > 2)
115 svn_wc__db_op_depth_for_upgrade(const char *local_relpath)
117 return relpath_depth(local_relpath);
121 /* Representation of a new base row for the NODES table */
122 typedef struct insert_base_baton_t {
123 /* common to all insertions into BASE */
124 svn_wc__db_status_t status;
125 svn_node_kind_t kind;
126 apr_int64_t repos_id;
127 const char *repos_relpath;
128 svn_revnum_t revision;
130 /* Only used when repos_id == INVALID_REPOS_ID */
131 const char *repos_root_url;
132 const char *repos_uuid;
134 /* common to all "normal" presence insertions */
135 const apr_hash_t *props;
136 svn_revnum_t changed_rev;
137 apr_time_t changed_date;
138 const char *changed_author;
139 const apr_hash_t *dav_cache;
141 /* for inserting directories */
142 const apr_array_header_t *children;
145 /* for inserting files */
146 const svn_checksum_t *checksum;
148 /* for inserting symlinks */
151 svn_boolean_t file_external;
153 /* may need to insert/update ACTUAL to record a conflict */
154 const svn_skel_t *conflict;
156 /* may need to insert/update ACTUAL to record new properties */
157 svn_boolean_t update_actual_props;
158 const apr_hash_t *new_actual_props;
160 /* A depth-first ordered array of svn_prop_inherited_item_t *
161 structures representing the properties inherited by the base
163 apr_array_header_t *iprops;
165 /* maybe we should copy information from a previous record? */
166 svn_boolean_t keep_recorded_info;
168 /* insert a base-deleted working node as well as a base node */
169 svn_boolean_t insert_base_deleted;
171 /* delete the current working nodes above BASE */
172 svn_boolean_t delete_working;
174 /* may have work items to queue in this transaction */
175 const svn_skel_t *work_items;
177 } insert_base_baton_t;
180 /* Representation of a new working row for the NODES table */
181 typedef struct insert_working_baton_t {
182 /* common to all insertions into WORKING (including NODE_DATA) */
183 svn_wc__db_status_t presence;
184 svn_node_kind_t kind;
187 /* common to all "normal" presence insertions */
188 const apr_hash_t *props;
189 svn_revnum_t changed_rev;
190 apr_time_t changed_date;
191 const char *changed_author;
192 apr_int64_t original_repos_id;
193 const char *original_repos_relpath;
194 svn_revnum_t original_revnum;
195 svn_boolean_t moved_here;
197 /* for inserting directories */
198 const apr_array_header_t *children;
201 /* for inserting (copied/moved-here) files */
202 const svn_checksum_t *checksum;
204 /* for inserting symlinks */
207 svn_boolean_t update_actual_props;
208 const apr_hash_t *new_actual_props;
210 /* may have work items to queue in this transaction */
211 const svn_skel_t *work_items;
213 /* may have conflict to install in this transaction */
214 const svn_skel_t *conflict;
216 /* If the value is > 0 and < op_depth, also insert a not-present
217 at op-depth NOT_PRESENT_OP_DEPTH, based on this same information */
218 int not_present_op_depth;
220 } insert_working_baton_t;
222 /* Representation of a new row for the EXTERNALS table */
223 typedef struct insert_external_baton_t {
224 /* common to all insertions into EXTERNALS */
225 svn_node_kind_t kind;
226 svn_wc__db_status_t presence;
228 /* The repository of the external */
229 apr_int64_t repos_id;
230 /* for file and symlink externals */
231 const char *repos_relpath;
232 svn_revnum_t revision;
234 /* Only used when repos_id == INVALID_REPOS_ID */
235 const char *repos_root_url;
236 const char *repos_uuid;
238 /* for file and symlink externals */
239 const apr_hash_t *props;
240 apr_array_header_t *iprops;
241 svn_revnum_t changed_rev;
242 apr_time_t changed_date;
243 const char *changed_author;
244 const apr_hash_t *dav_cache;
246 /* for inserting files */
247 const svn_checksum_t *checksum;
249 /* for inserting symlinks */
252 const char *record_ancestor_relpath;
253 const char *recorded_repos_relpath;
254 svn_revnum_t recorded_peg_revision;
255 svn_revnum_t recorded_revision;
257 /* may need to insert/update ACTUAL to record a conflict */
258 const svn_skel_t *conflict;
260 /* may need to insert/update ACTUAL to record new properties */
261 svn_boolean_t update_actual_props;
262 const apr_hash_t *new_actual_props;
264 /* maybe we should copy information from a previous record? */
265 svn_boolean_t keep_recorded_info;
267 /* may have work items to queue in this transaction */
268 const svn_skel_t *work_items;
270 } insert_external_baton_t;
273 /* Forward declarations */
275 add_work_items(svn_sqlite__db_t *sdb,
276 const svn_skel_t *skel,
277 apr_pool_t *scratch_pool);
280 set_actual_props(apr_int64_t wc_id,
281 const char *local_relpath,
283 svn_sqlite__db_t *db,
284 apr_pool_t *scratch_pool);
287 insert_incomplete_children(svn_sqlite__db_t *sdb,
289 const char *local_relpath,
290 apr_int64_t repos_id,
291 const char *repos_relpath,
292 svn_revnum_t revision,
293 const apr_array_header_t *children,
295 apr_pool_t *scratch_pool);
298 db_read_pristine_props(apr_hash_t **props,
299 svn_wc__db_wcroot_t *wcroot,
300 const char *local_relpath,
301 svn_boolean_t deleted_ok,
302 apr_pool_t *result_pool,
303 apr_pool_t *scratch_pool);
306 read_info(svn_wc__db_status_t *status,
307 svn_node_kind_t *kind,
308 svn_revnum_t *revision,
309 const char **repos_relpath,
310 apr_int64_t *repos_id,
311 svn_revnum_t *changed_rev,
312 apr_time_t *changed_date,
313 const char **changed_author,
315 const svn_checksum_t **checksum,
317 const char **original_repos_relpath,
318 apr_int64_t *original_repos_id,
319 svn_revnum_t *original_revision,
320 svn_wc__db_lock_t **lock,
321 svn_filesize_t *recorded_size,
322 apr_time_t *recorded_time,
323 const char **changelist,
324 svn_boolean_t *conflicted,
325 svn_boolean_t *op_root,
326 svn_boolean_t *had_props,
327 svn_boolean_t *props_mod,
328 svn_boolean_t *have_base,
329 svn_boolean_t *have_more_work,
330 svn_boolean_t *have_work,
331 svn_wc__db_wcroot_t *wcroot,
332 const char *local_relpath,
333 apr_pool_t *result_pool,
334 apr_pool_t *scratch_pool);
337 scan_addition(svn_wc__db_status_t *status,
338 const char **op_root_relpath,
339 const char **repos_relpath,
340 apr_int64_t *repos_id,
341 const char **original_repos_relpath,
342 apr_int64_t *original_repos_id,
343 svn_revnum_t *original_revision,
344 const char **moved_from_relpath,
345 const char **moved_from_op_root_relpath,
346 int *moved_from_op_depth,
347 svn_wc__db_wcroot_t *wcroot,
348 const char *local_relpath,
349 apr_pool_t *result_pool,
350 apr_pool_t *scratch_pool);
353 convert_to_working_status(svn_wc__db_status_t *working_status,
354 svn_wc__db_status_t status);
357 wclock_owns_lock(svn_boolean_t *own_lock,
358 svn_wc__db_wcroot_t *wcroot,
359 const char *local_relpath,
361 apr_pool_t *scratch_pool);
364 db_is_switched(svn_boolean_t *is_switched,
365 svn_node_kind_t *kind,
366 svn_wc__db_wcroot_t *wcroot,
367 const char *local_relpath,
368 apr_pool_t *scratch_pool);
371 /* Return the absolute path, in local path style, of LOCAL_RELPATH
374 path_for_error_message(const svn_wc__db_wcroot_t *wcroot,
375 const char *local_relpath,
376 apr_pool_t *result_pool)
378 const char *local_abspath
379 = svn_dirent_join(wcroot->abspath, local_relpath, result_pool);
381 return svn_dirent_local_style(local_abspath, result_pool);
385 /* Return a file size from column SLOT of the SQLITE statement STMT, or
386 SVN_INVALID_FILESIZE if the column value is NULL. */
387 static svn_filesize_t
388 get_recorded_size(svn_sqlite__stmt_t *stmt, int slot)
390 if (svn_sqlite__column_is_null(stmt, slot))
391 return SVN_INVALID_FILESIZE;
392 return svn_sqlite__column_int64(stmt, slot);
396 /* Return a lock info structure constructed from the given columns of the
397 SQLITE statement STMT, or return NULL if the token column value is null. */
398 static svn_wc__db_lock_t *
399 lock_from_columns(svn_sqlite__stmt_t *stmt,
404 apr_pool_t *result_pool)
406 svn_wc__db_lock_t *lock;
408 if (svn_sqlite__column_is_null(stmt, col_token))
414 lock = apr_pcalloc(result_pool, sizeof(svn_wc__db_lock_t));
415 lock->token = svn_sqlite__column_text(stmt, col_token, result_pool);
416 lock->owner = svn_sqlite__column_text(stmt, col_owner, result_pool);
417 lock->comment = svn_sqlite__column_text(stmt, col_comment, result_pool);
418 lock->date = svn_sqlite__column_int64(stmt, col_date);
425 svn_wc__db_fetch_repos_info(const char **repos_root_url,
426 const char **repos_uuid,
427 svn_sqlite__db_t *sdb,
428 apr_int64_t repos_id,
429 apr_pool_t *result_pool)
431 svn_sqlite__stmt_t *stmt;
432 svn_boolean_t have_row;
434 if (!repos_root_url && !repos_uuid)
437 if (repos_id == INVALID_REPOS_ID)
440 *repos_root_url = NULL;
446 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
447 STMT_SELECT_REPOSITORY_BY_ID));
448 SVN_ERR(svn_sqlite__bindf(stmt, "i", repos_id));
449 SVN_ERR(svn_sqlite__step(&have_row, stmt));
451 return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
452 _("No REPOSITORY table entry for id '%ld'"),
456 *repos_root_url = svn_sqlite__column_text(stmt, 0, result_pool);
458 *repos_uuid = svn_sqlite__column_text(stmt, 1, result_pool);
460 return svn_error_trace(svn_sqlite__reset(stmt));
463 /* Set *REPOS_ID, *REVISION and *REPOS_RELPATH from the given columns of the
464 SQLITE statement STMT, or to NULL/SVN_INVALID_REVNUM if the respective
465 column value is null. Any of the output parameters may be NULL if not
468 repos_location_from_columns(apr_int64_t *repos_id,
469 svn_revnum_t *revision,
470 const char **repos_relpath,
471 svn_sqlite__stmt_t *stmt,
474 int col_repos_relpath,
475 apr_pool_t *result_pool)
479 /* Fetch repository information via REPOS_ID. */
480 if (svn_sqlite__column_is_null(stmt, col_repos_id))
481 *repos_id = INVALID_REPOS_ID;
483 *repos_id = svn_sqlite__column_int64(stmt, col_repos_id);
487 *revision = svn_sqlite__column_revnum(stmt, col_revision);
491 *repos_relpath = svn_sqlite__column_text(stmt, col_repos_relpath,
497 /* Get the statement given by STMT_IDX, and bind the appropriate wc_id and
498 local_relpath based upon LOCAL_ABSPATH. Store it in *STMT, and use
499 SCRATCH_POOL for temporary allocations.
501 Note: WC_ID and LOCAL_RELPATH must be arguments 1 and 2 in the statement. */
503 get_statement_for_path(svn_sqlite__stmt_t **stmt,
505 const char *local_abspath,
507 apr_pool_t *scratch_pool)
509 svn_wc__db_wcroot_t *wcroot;
510 const char *local_relpath;
512 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
514 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
515 local_abspath, scratch_pool, scratch_pool));
516 VERIFY_USABLE_WCROOT(wcroot);
518 SVN_ERR(svn_sqlite__get_statement(stmt, wcroot->sdb, stmt_idx));
519 SVN_ERR(svn_sqlite__bindf(*stmt, "is", wcroot->wc_id, local_relpath));
525 /* For a given REPOS_ROOT_URL/REPOS_UUID pair, return the existing REPOS_ID
526 value. If one does not exist, then create a new one. */
528 create_repos_id(apr_int64_t *repos_id,
529 const char *repos_root_url,
530 const char *repos_uuid,
531 svn_sqlite__db_t *sdb,
532 apr_pool_t *scratch_pool)
534 svn_sqlite__stmt_t *get_stmt;
535 svn_sqlite__stmt_t *insert_stmt;
536 svn_boolean_t have_row;
538 SVN_ERR(svn_sqlite__get_statement(&get_stmt, sdb, STMT_SELECT_REPOSITORY));
539 SVN_ERR(svn_sqlite__bindf(get_stmt, "s", repos_root_url));
540 SVN_ERR(svn_sqlite__step(&have_row, get_stmt));
544 *repos_id = svn_sqlite__column_int64(get_stmt, 0);
545 return svn_error_trace(svn_sqlite__reset(get_stmt));
547 SVN_ERR(svn_sqlite__reset(get_stmt));
549 /* NOTE: strictly speaking, there is a race condition between the
550 above query and the insertion below. We're simply going to ignore
551 that, as it means two processes are *modifying* the working copy
552 at the same time, *and* new repositores are becoming visible.
553 This is rare enough, let alone the miniscule chance of hitting
554 this race condition. Further, simply failing out will leave the
555 database in a consistent state, and the user can just re-run the
558 SVN_ERR(svn_sqlite__get_statement(&insert_stmt, sdb,
559 STMT_INSERT_REPOSITORY));
560 SVN_ERR(svn_sqlite__bindf(insert_stmt, "ss", repos_root_url, repos_uuid));
561 return svn_error_trace(svn_sqlite__insert(repos_id, insert_stmt));
565 /* Initialize the baton with appropriate "blank" values. This allows the
566 insertion function to leave certain columns null. */
568 blank_ibb(insert_base_baton_t *pibb)
570 memset(pibb, 0, sizeof(*pibb));
571 pibb->revision = SVN_INVALID_REVNUM;
572 pibb->changed_rev = SVN_INVALID_REVNUM;
573 pibb->depth = svn_depth_infinity;
574 pibb->repos_id = INVALID_REPOS_ID;
579 svn_wc__db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot,
580 const char *local_relpath,
581 svn_node_kind_t kind,
583 apr_pool_t *scratch_pool)
585 svn_boolean_t have_row;
586 svn_sqlite__stmt_t *stmt;
588 const char *parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
590 SVN_ERR_ASSERT(local_relpath[0]);
592 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
593 STMT_SELECT_LOWEST_WORKING_NODE));
594 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, parent_relpath,
596 SVN_ERR(svn_sqlite__step(&have_row, stmt));
598 parent_op_depth = svn_sqlite__column_int(stmt, 0);
599 SVN_ERR(svn_sqlite__reset(stmt));
602 int existing_op_depth;
604 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
606 SVN_ERR(svn_sqlite__step(&have_row, stmt));
608 existing_op_depth = svn_sqlite__column_int(stmt, 0);
609 SVN_ERR(svn_sqlite__reset(stmt));
610 if (!have_row || parent_op_depth < existing_op_depth)
612 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
613 STMT_INSTALL_WORKING_NODE_FOR_DELETE));
614 SVN_ERR(svn_sqlite__bindf(stmt, "isdst", wcroot->wc_id,
615 local_relpath, parent_op_depth,
616 parent_relpath, kind_map, kind));
617 SVN_ERR(svn_sqlite__update(NULL, stmt));
625 /* This is the reverse of svn_wc__db_extend_parent_delete.
627 When removing a node if the parent has a higher working node then
628 the parent node and this node are both deleted or replaced and any
629 delete over this node must be removed.
631 This function (like most wcroot functions) assumes that its caller
632 only uses this function within an sqlite transaction if atomic
636 svn_wc__db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot,
637 const char *local_relpath,
639 apr_pool_t *scratch_pool)
641 svn_sqlite__stmt_t *stmt;
642 svn_boolean_t have_row;
644 svn_wc__db_status_t presence;
645 const char *moved_to;
647 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
648 STMT_SELECT_LOWEST_WORKING_NODE));
649 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
651 SVN_ERR(svn_sqlite__step(&have_row, stmt));
654 return svn_error_trace(svn_sqlite__reset(stmt));
656 working_depth = svn_sqlite__column_int(stmt, 0);
657 presence = svn_sqlite__column_token(stmt, 1, presence_map);
658 moved_to = svn_sqlite__column_text(stmt, 3, scratch_pool);
660 SVN_ERR(svn_sqlite__reset(stmt));
664 /* Turn the move into a copy to keep the NODES table valid */
665 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
666 STMT_CLEAR_MOVED_HERE_RECURSIVE));
667 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
668 moved_to, relpath_depth(moved_to)));
669 SVN_ERR(svn_sqlite__step_done(stmt));
671 /* This leaves just the moved_to information on the origin,
672 which we will remove in the next step */
675 if (presence == svn_wc__db_status_base_deleted)
677 /* Nothing left to shadow; remove the base-deleted node */
678 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_NODE));
682 /* Clear moved to information, as this node is no longer base-deleted */
683 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
684 STMT_CLEAR_MOVED_TO_RELPATH));
688 /* Nothing to update */
692 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
695 return svn_error_trace(svn_sqlite__update(NULL, stmt));
700 /* Insert the base row represented by (insert_base_baton_t *) BATON. */
702 insert_base_node(const insert_base_baton_t *pibb,
703 svn_wc__db_wcroot_t *wcroot,
704 const char *local_relpath,
705 apr_pool_t *scratch_pool)
707 apr_int64_t repos_id = pibb->repos_id;
708 svn_sqlite__stmt_t *stmt;
709 svn_filesize_t recorded_size = SVN_INVALID_FILESIZE;
710 apr_int64_t recorded_time;
711 svn_boolean_t present;
713 /* The directory at the WCROOT has a NULL parent_relpath. Otherwise,
714 bind the appropriate parent_relpath. */
715 const char *parent_relpath =
716 (*local_relpath == '\0') ? NULL
717 : svn_relpath_dirname(local_relpath, scratch_pool);
719 if (pibb->repos_id == INVALID_REPOS_ID)
720 SVN_ERR(create_repos_id(&repos_id, pibb->repos_root_url, pibb->repos_uuid,
721 wcroot->sdb, scratch_pool));
723 SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
724 SVN_ERR_ASSERT(pibb->repos_relpath != NULL);
726 if (pibb->keep_recorded_info)
728 svn_boolean_t have_row;
729 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
730 STMT_SELECT_BASE_NODE));
731 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
732 SVN_ERR(svn_sqlite__step(&have_row, stmt));
735 /* Preserve size and modification time if caller asked us to. */
736 recorded_size = get_recorded_size(stmt, 6);
737 recorded_time = svn_sqlite__column_int64(stmt, 12);
739 SVN_ERR(svn_sqlite__reset(stmt));
742 present = (pibb->status == svn_wc__db_status_normal
743 || pibb->status == svn_wc__db_status_incomplete);
745 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
746 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisr"
748 "isnnnnns", /* 12 - 19 */
749 wcroot->wc_id, /* 1 */
750 local_relpath, /* 2 */
751 0, /* op_depth is 0 for base */
752 parent_relpath, /* 4 */
756 presence_map, pibb->status, /* 8 */
757 (pibb->kind == svn_node_dir && present) /* 9 */
758 ? svn_token__to_word(depth_map, pibb->depth)
760 kind_map, pibb->kind, /* 10 */
761 pibb->changed_rev, /* 11 */
762 pibb->changed_date, /* 12 */
763 pibb->changed_author, /* 13 */
764 (pibb->kind == svn_node_symlink && present) ?
765 pibb->target : NULL)); /* 19 */
766 if (pibb->kind == svn_node_file && present)
769 && pibb->status != svn_wc__db_status_not_present
770 && pibb->status != svn_wc__db_status_excluded
771 && pibb->status != svn_wc__db_status_server_excluded)
772 return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
773 _("The file '%s' has no checksum."),
774 path_for_error_message(wcroot, local_relpath,
777 SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, pibb->checksum,
780 if (recorded_size != SVN_INVALID_FILESIZE)
782 SVN_ERR(svn_sqlite__bind_int64(stmt, 16, recorded_size));
783 SVN_ERR(svn_sqlite__bind_int64(stmt, 17, recorded_time));
787 /* Set properties. Must be null if presence not normal or incomplete. */
788 assert(pibb->status == svn_wc__db_status_normal
789 || pibb->status == svn_wc__db_status_incomplete
790 || pibb->props == NULL);
793 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, pibb->props,
796 SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, pibb->iprops,
801 SVN_ERR(svn_sqlite__bind_properties(stmt, 18, pibb->dav_cache,
804 if (pibb->file_external)
805 SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
807 SVN_ERR(svn_sqlite__insert(NULL, stmt));
809 if (pibb->update_actual_props)
811 /* Cast away const, to allow calling property helpers */
812 apr_hash_t *base_props = (apr_hash_t *)pibb->props;
813 apr_hash_t *new_actual_props = (apr_hash_t *)pibb->new_actual_props;
815 if (base_props != NULL
816 && new_actual_props != NULL
817 && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
819 apr_array_header_t *diffs;
821 SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
824 if (diffs->nelts == 0)
825 new_actual_props = NULL;
828 SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props,
829 wcroot->sdb, scratch_pool));
832 if (pibb->kind == svn_node_dir && pibb->children)
833 SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
842 /* When this is not the root node, check shadowing behavior */
846 && ((pibb->status == svn_wc__db_status_normal)
847 || (pibb->status == svn_wc__db_status_incomplete))
848 && ! pibb->file_external)
850 SVN_ERR(svn_wc__db_extend_parent_delete(wcroot, local_relpath,
854 else if (pibb->status == svn_wc__db_status_not_present
855 || pibb->status == svn_wc__db_status_server_excluded
856 || pibb->status == svn_wc__db_status_excluded)
858 SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
863 if (pibb->delete_working)
865 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
866 STMT_DELETE_WORKING_NODE));
867 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
868 SVN_ERR(svn_sqlite__step_done(stmt));
870 if (pibb->insert_base_deleted)
872 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
873 STMT_INSERT_DELETE_FROM_BASE));
874 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
875 wcroot->wc_id, local_relpath,
876 relpath_depth(local_relpath)));
877 SVN_ERR(svn_sqlite__step_done(stmt));
880 SVN_ERR(add_work_items(wcroot->sdb, pibb->work_items, scratch_pool));
882 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
883 pibb->conflict, scratch_pool));
889 /* Initialize the baton with appropriate "blank" values. This allows the
890 insertion function to leave certain columns null. */
892 blank_iwb(insert_working_baton_t *piwb)
894 memset(piwb, 0, sizeof(*piwb));
895 piwb->changed_rev = SVN_INVALID_REVNUM;
896 piwb->depth = svn_depth_infinity;
898 /* ORIGINAL_REPOS_ID and ORIGINAL_REVNUM could use some kind of "nil"
899 value, but... meh. We'll avoid them if ORIGINAL_REPOS_RELPATH==NULL. */
903 /* Insert a row in NODES for each (const char *) child name in CHILDREN,
904 whose parent directory is LOCAL_RELPATH, at op_depth=OP_DEPTH. Set each
905 child's presence to 'incomplete', kind to 'unknown', repos_id to REPOS_ID,
906 repos_path by appending the child name to REPOS_PATH, and revision to
907 REVISION (which should match the parent's revision).
909 If REPOS_ID is INVALID_REPOS_ID, set each child's repos_id to null. */
911 insert_incomplete_children(svn_sqlite__db_t *sdb,
913 const char *local_relpath,
914 apr_int64_t repos_id,
915 const char *repos_path,
916 svn_revnum_t revision,
917 const apr_array_header_t *children,
919 apr_pool_t *scratch_pool)
921 svn_sqlite__stmt_t *stmt;
923 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
924 apr_hash_t *moved_to_relpaths = apr_hash_make(scratch_pool);
926 SVN_ERR_ASSERT(repos_path != NULL || op_depth > 0);
927 SVN_ERR_ASSERT((repos_id != INVALID_REPOS_ID)
928 == (repos_path != NULL));
930 /* If we're inserting WORKING nodes, we might be replacing existing
931 * nodes which were moved-away. We need to retain the moved-to relpath of
932 * such nodes in order not to lose move information during replace. */
935 for (i = children->nelts; i--; )
937 const char *name = APR_ARRAY_IDX(children, i, const char *);
938 svn_boolean_t have_row;
940 svn_pool_clear(iterpool);
942 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
943 STMT_SELECT_WORKING_NODE));
944 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id,
945 svn_relpath_join(local_relpath, name,
947 SVN_ERR(svn_sqlite__step(&have_row, stmt));
948 if (have_row && !svn_sqlite__column_is_null(stmt, 14))
949 svn_hash_sets(moved_to_relpaths, name,
950 svn_sqlite__column_text(stmt, 14, scratch_pool));
952 SVN_ERR(svn_sqlite__reset(stmt));
956 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
958 for (i = children->nelts; i--; )
960 const char *name = APR_ARRAY_IDX(children, i, const char *);
962 svn_pool_clear(iterpool);
964 SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnrsnsnnnnnnnnnnsn",
966 svn_relpath_join(local_relpath, name,
971 "incomplete", /* 8, presence */
972 "unknown", /* 10, kind */
974 svn_hash_gets(moved_to_relpaths, name)));
975 if (repos_id != INVALID_REPOS_ID)
977 SVN_ERR(svn_sqlite__bind_int64(stmt, 5, repos_id));
978 SVN_ERR(svn_sqlite__bind_text(stmt, 6,
979 svn_relpath_join(repos_path, name,
983 SVN_ERR(svn_sqlite__insert(NULL, stmt));
986 svn_pool_destroy(iterpool);
992 /* Insert the working row represented by (insert_working_baton_t *) BATON. */
994 insert_working_node(const insert_working_baton_t *piwb,
995 svn_wc__db_wcroot_t *wcroot,
996 const char *local_relpath,
997 apr_pool_t *scratch_pool)
999 const char *parent_relpath;
1000 const char *moved_to_relpath = NULL;
1001 svn_sqlite__stmt_t *stmt;
1002 svn_boolean_t have_row;
1003 svn_boolean_t present;
1005 SVN_ERR_ASSERT(piwb->op_depth > 0);
1007 /* We cannot insert a WORKING_NODE row at the wcroot. */
1008 SVN_ERR_ASSERT(*local_relpath != '\0');
1009 parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
1011 /* Preserve existing moved-to information for this relpath,
1012 * which might exist in case we're replacing an existing base-deleted
1014 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
1015 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1017 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1019 moved_to_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
1020 SVN_ERR(svn_sqlite__reset(stmt));
1022 present = (piwb->presence == svn_wc__db_status_normal
1023 || piwb->presence == svn_wc__db_status_incomplete);
1025 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
1026 SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnntstrisn"
1027 "nnnn" /* properties translated_size last_mod_time dav_cache */
1028 "sns", /* symlink_target, file_external, moved_to */
1029 wcroot->wc_id, local_relpath,
1032 presence_map, piwb->presence,
1033 (piwb->kind == svn_node_dir && present)
1034 ? svn_token__to_word(depth_map, piwb->depth) : NULL,
1035 kind_map, piwb->kind,
1038 piwb->changed_author,
1039 /* Note: incomplete nodes may have a NULL target. */
1040 (piwb->kind == svn_node_symlink && present)
1041 ? piwb->target : NULL,
1044 if (piwb->moved_here)
1046 SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
1049 if (piwb->kind == svn_node_file && present)
1051 SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, piwb->checksum,
1055 if (piwb->original_repos_relpath != NULL)
1057 SVN_ERR(svn_sqlite__bind_int64(stmt, 5, piwb->original_repos_id));
1058 SVN_ERR(svn_sqlite__bind_text(stmt, 6, piwb->original_repos_relpath));
1059 SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, piwb->original_revnum));
1062 /* Set properties. Must be null if presence not normal or incomplete. */
1063 assert(piwb->presence == svn_wc__db_status_normal
1064 || piwb->presence == svn_wc__db_status_incomplete
1065 || piwb->props == NULL);
1066 if (present && piwb->original_repos_relpath)
1067 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, piwb->props, scratch_pool));
1069 SVN_ERR(svn_sqlite__insert(NULL, stmt));
1071 /* Insert incomplete children, if specified.
1072 The children are part of the same op and so have the same op_depth.
1073 (The only time we'd want a different depth is during a recursive
1074 simple add, but we never insert children here during a simple add.) */
1075 if (piwb->kind == svn_node_dir && piwb->children)
1076 SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
1078 INVALID_REPOS_ID /* inherit repos_id */,
1079 NULL /* inherit repos_path */,
1080 piwb->original_revnum,
1085 if (piwb->update_actual_props)
1087 /* Cast away const, to allow calling property helpers */
1088 apr_hash_t *base_props = (apr_hash_t *)piwb->props;
1089 apr_hash_t *new_actual_props = (apr_hash_t *)piwb->new_actual_props;
1091 if (base_props != NULL
1092 && new_actual_props != NULL
1093 && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
1095 apr_array_header_t *diffs;
1097 SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
1100 if (diffs->nelts == 0)
1101 new_actual_props = NULL;
1104 SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props,
1105 wcroot->sdb, scratch_pool));
1108 if (piwb->kind == svn_node_dir)
1110 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1111 STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST));
1112 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1113 SVN_ERR(svn_sqlite__step_done(stmt));
1115 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1116 STMT_DELETE_ACTUAL_EMPTY));
1117 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1118 SVN_ERR(svn_sqlite__step_done(stmt));
1121 if (piwb->not_present_op_depth > 0
1122 && piwb->not_present_op_depth < piwb->op_depth)
1124 /* And also insert a not-present node to tell the commit processing that
1125 a child of the parent node was not copied. */
1126 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1129 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
1130 wcroot->wc_id, local_relpath,
1131 piwb->not_present_op_depth, parent_relpath,
1132 piwb->original_repos_id,
1133 piwb->original_repos_relpath,
1134 piwb->original_revnum,
1135 presence_map, svn_wc__db_status_not_present,
1137 kind_map, piwb->kind));
1139 SVN_ERR(svn_sqlite__step_done(stmt));
1142 SVN_ERR(add_work_items(wcroot->sdb, piwb->work_items, scratch_pool));
1144 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
1145 piwb->conflict, scratch_pool));
1147 return SVN_NO_ERROR;
1151 /* Each name is allocated in RESULT_POOL and stored into CHILDREN as a key
1152 pointed to the same name. */
1153 static svn_error_t *
1154 add_children_to_hash(apr_hash_t *children,
1156 svn_sqlite__db_t *sdb,
1158 const char *parent_relpath,
1159 apr_pool_t *result_pool)
1161 svn_sqlite__stmt_t *stmt;
1162 svn_boolean_t have_row;
1164 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, stmt_idx));
1165 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, parent_relpath));
1166 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1169 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1170 const char *name = svn_relpath_basename(child_relpath, result_pool);
1172 svn_hash_sets(children, name, name);
1174 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1177 return svn_sqlite__reset(stmt);
1181 /* Set *CHILDREN to a new array of the (const char *) basenames of the
1182 immediate children, whatever their status, of the working node at
1184 static svn_error_t *
1185 gather_children2(const apr_array_header_t **children,
1186 svn_wc__db_wcroot_t *wcroot,
1187 const char *local_relpath,
1188 apr_pool_t *result_pool,
1189 apr_pool_t *scratch_pool)
1191 apr_hash_t *names_hash = apr_hash_make(scratch_pool);
1192 apr_array_header_t *names_array;
1194 /* All of the names get allocated in RESULT_POOL. It
1195 appears to be faster to use the hash to remove duplicates than to
1196 use DISTINCT in the SQL query. */
1197 SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_WORKING_CHILDREN,
1198 wcroot->sdb, wcroot->wc_id,
1199 local_relpath, result_pool));
1201 SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool));
1202 *children = names_array;
1203 return SVN_NO_ERROR;
1206 /* Return in *CHILDREN all of the children of the directory LOCAL_RELPATH,
1207 of any status, in all op-depths in the NODES table. */
1208 static svn_error_t *
1209 gather_children(const apr_array_header_t **children,
1210 svn_wc__db_wcroot_t *wcroot,
1211 const char *local_relpath,
1212 apr_pool_t *result_pool,
1213 apr_pool_t *scratch_pool)
1215 apr_hash_t *names_hash = apr_hash_make(scratch_pool);
1216 apr_array_header_t *names_array;
1218 /* All of the names get allocated in RESULT_POOL. It
1219 appears to be faster to use the hash to remove duplicates than to
1220 use DISTINCT in the SQL query. */
1221 SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_NODE_CHILDREN,
1222 wcroot->sdb, wcroot->wc_id,
1223 local_relpath, result_pool));
1225 SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool));
1226 *children = names_array;
1227 return SVN_NO_ERROR;
1231 /* Set *CHILDREN to a new array of (const char *) names of the children of
1232 the repository directory corresponding to WCROOT:LOCAL_RELPATH:OP_DEPTH -
1233 that is, only the children that are at the same op-depth as their parent. */
1234 static svn_error_t *
1235 gather_repo_children(const apr_array_header_t **children,
1236 svn_wc__db_wcroot_t *wcroot,
1237 const char *local_relpath,
1239 apr_pool_t *result_pool,
1240 apr_pool_t *scratch_pool)
1242 apr_array_header_t *result
1243 = apr_array_make(result_pool, 0, sizeof(const char *));
1244 svn_sqlite__stmt_t *stmt;
1245 svn_boolean_t have_row;
1247 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1248 STMT_SELECT_OP_DEPTH_CHILDREN));
1249 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1251 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1254 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1256 /* Allocate the name in RESULT_POOL so we won't have to copy it. */
1257 APR_ARRAY_PUSH(result, const char *)
1258 = svn_relpath_basename(child_relpath, result_pool);
1260 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1262 SVN_ERR(svn_sqlite__reset(stmt));
1265 return SVN_NO_ERROR;
1269 svn_wc__db_get_children_op_depth(apr_hash_t **children,
1270 svn_wc__db_wcroot_t *wcroot,
1271 const char *local_relpath,
1273 apr_pool_t *result_pool,
1274 apr_pool_t *scratch_pool)
1276 svn_sqlite__stmt_t *stmt;
1277 svn_boolean_t have_row;
1279 *children = apr_hash_make(result_pool);
1281 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1282 STMT_SELECT_OP_DEPTH_CHILDREN));
1283 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1285 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1288 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1289 svn_node_kind_t *child_kind = apr_palloc(result_pool, sizeof(svn_node_kind_t));
1291 *child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
1292 svn_hash_sets(*children,
1293 svn_relpath_basename(child_relpath, result_pool),
1296 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1298 SVN_ERR(svn_sqlite__reset(stmt));
1300 return SVN_NO_ERROR;
1304 /* Return TRUE if CHILD_ABSPATH is an immediate child of PARENT_ABSPATH.
1305 * Else, return FALSE. */
1306 static svn_boolean_t
1307 is_immediate_child_path(const char *parent_abspath, const char *child_abspath)
1309 const char *local_relpath = svn_dirent_skip_ancestor(parent_abspath,
1312 /* To be an immediate child local_relpath should have one (not empty)
1314 return local_relpath && *local_relpath && !strchr(local_relpath, '/');
1318 /* Remove the access baton for LOCAL_ABSPATH from ACCESS_CACHE. */
1320 remove_from_access_cache(apr_hash_t *access_cache,
1321 const char *local_abspath)
1323 svn_wc_adm_access_t *adm_access;
1325 adm_access = svn_hash_gets(access_cache, local_abspath);
1327 svn_wc__adm_access_set_entries(adm_access, NULL);
1331 /* Flush the access baton for LOCAL_ABSPATH, and any of its children up to
1332 * the specified DEPTH, from the access baton cache in WCROOT.
1333 * Also flush the access baton for the parent of LOCAL_ABSPATH.I
1335 * This function must be called when the access baton cache goes stale,
1336 * i.e. data about LOCAL_ABSPATH will need to be read again from disk.
1338 * Use SCRATCH_POOL for temporary allocations. */
1339 static svn_error_t *
1340 flush_entries(svn_wc__db_wcroot_t *wcroot,
1341 const char *local_abspath,
1343 apr_pool_t *scratch_pool)
1345 const char *parent_abspath;
1347 if (apr_hash_count(wcroot->access_cache) == 0)
1348 return SVN_NO_ERROR;
1350 remove_from_access_cache(wcroot->access_cache, local_abspath);
1352 if (depth > svn_depth_empty)
1354 apr_hash_index_t *hi;
1356 /* Flush access batons of children within the specified depth. */
1357 for (hi = apr_hash_first(scratch_pool, wcroot->access_cache);
1359 hi = apr_hash_next(hi))
1361 const char *item_abspath = svn__apr_hash_index_key(hi);
1363 if ((depth == svn_depth_files || depth == svn_depth_immediates) &&
1364 is_immediate_child_path(local_abspath, item_abspath))
1366 remove_from_access_cache(wcroot->access_cache, item_abspath);
1368 else if (depth == svn_depth_infinity &&
1369 svn_dirent_is_ancestor(local_abspath, item_abspath))
1371 remove_from_access_cache(wcroot->access_cache, item_abspath);
1376 /* We're going to be overly aggressive here and just flush the parent
1377 without doing much checking. This may hurt performance for
1378 legacy API consumers, but that's not our problem. :) */
1379 parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
1380 remove_from_access_cache(wcroot->access_cache, parent_abspath);
1382 return SVN_NO_ERROR;
1386 /* Add a single WORK_ITEM into the given SDB's WORK_QUEUE table. This does
1387 not perform its work within a transaction, assuming the caller will
1389 static svn_error_t *
1390 add_single_work_item(svn_sqlite__db_t *sdb,
1391 const svn_skel_t *work_item,
1392 apr_pool_t *scratch_pool)
1394 svn_stringbuf_t *serialized;
1395 svn_sqlite__stmt_t *stmt;
1397 serialized = svn_skel__unparse(work_item, scratch_pool);
1398 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_WORK_ITEM));
1399 SVN_ERR(svn_sqlite__bind_blob(stmt, 1, serialized->data, serialized->len));
1400 return svn_error_trace(svn_sqlite__insert(NULL, stmt));
1404 /* Add work item(s) to the given SDB. Also see add_single_work_item(). This
1405 SKEL is usually passed to the various wc_db operation functions. It may
1406 be NULL, indicating no additional work items are needed, it may be a
1407 single work item, or it may be a list of work items. */
1408 static svn_error_t *
1409 add_work_items(svn_sqlite__db_t *sdb,
1410 const svn_skel_t *skel,
1411 apr_pool_t *scratch_pool)
1413 apr_pool_t *iterpool;
1415 /* Maybe there are no work items to insert. */
1417 return SVN_NO_ERROR;
1419 /* Should have a list. */
1420 SVN_ERR_ASSERT(!skel->is_atom);
1422 /* Is the list a single work item? Or a list of work items? */
1423 if (SVN_WC__SINGLE_WORK_ITEM(skel))
1424 return svn_error_trace(add_single_work_item(sdb, skel, scratch_pool));
1426 /* SKEL is a list-of-lists, aka list of work items. */
1428 iterpool = svn_pool_create(scratch_pool);
1429 for (skel = skel->children; skel; skel = skel->next)
1431 svn_pool_clear(iterpool);
1433 SVN_ERR(add_single_work_item(sdb, skel, iterpool));
1435 svn_pool_destroy(iterpool);
1437 return SVN_NO_ERROR;
1441 /* Determine whether the node exists for a given WCROOT and LOCAL_RELPATH. */
1442 static svn_error_t *
1443 does_node_exist(svn_boolean_t *exists,
1444 const svn_wc__db_wcroot_t *wcroot,
1445 const char *local_relpath)
1447 svn_sqlite__stmt_t *stmt;
1449 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DOES_NODE_EXIST));
1450 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1451 SVN_ERR(svn_sqlite__step(exists, stmt));
1453 return svn_error_trace(svn_sqlite__reset(stmt));
1457 svn_wc__db_install_schema_statistics(svn_sqlite__db_t *sdb,
1458 apr_pool_t *scratch_pool)
1460 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_INSTALL_SCHEMA_STATISTICS));
1462 return SVN_NO_ERROR;
1465 /* Helper for create_db(). Initializes our wc.db schema.
1467 static svn_error_t *
1468 init_db(/* output values */
1469 apr_int64_t *repos_id,
1472 svn_sqlite__db_t *db,
1473 const char *repos_root_url,
1474 const char *repos_uuid,
1475 const char *root_node_repos_relpath,
1476 svn_revnum_t root_node_revision,
1477 svn_depth_t root_node_depth,
1478 apr_pool_t *scratch_pool)
1480 svn_sqlite__stmt_t *stmt;
1482 /* Create the database's schema. */
1483 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_SCHEMA));
1484 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES));
1485 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES_TRIGGERS));
1486 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_EXTERNALS));
1488 /* Insert the repository. */
1489 SVN_ERR(create_repos_id(repos_id, repos_root_url, repos_uuid,
1492 SVN_ERR(svn_wc__db_install_schema_statistics(db, scratch_pool));
1494 /* Insert the wcroot. */
1495 /* ### Right now, this just assumes wc metadata is being stored locally. */
1496 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT));
1497 SVN_ERR(svn_sqlite__insert(wc_id, stmt));
1499 if (root_node_repos_relpath)
1501 svn_wc__db_status_t status = svn_wc__db_status_normal;
1503 if (root_node_revision > 0)
1504 status = svn_wc__db_status_incomplete; /* Will be filled by update */
1506 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_NODE));
1507 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtst",
1510 0, /* op_depth is 0 for base */
1513 root_node_repos_relpath,
1515 presence_map, status, /* 8 */
1516 svn_token__to_word(depth_map,
1518 kind_map, svn_node_dir /* 10 */));
1520 SVN_ERR(svn_sqlite__insert(NULL, stmt));
1523 return SVN_NO_ERROR;
1526 /* Create an sqlite database at DIR_ABSPATH/SDB_FNAME and insert
1527 records for REPOS_ID (using REPOS_ROOT_URL and REPOS_UUID) into
1528 REPOSITORY and for WC_ID into WCROOT. Return the DB connection
1531 If ROOT_NODE_REPOS_RELPATH is not NULL, insert a BASE node at
1532 the working copy root with repository relpath ROOT_NODE_REPOS_RELPATH,
1533 revision ROOT_NODE_REVISION and depth ROOT_NODE_DEPTH.
1535 static svn_error_t *
1536 create_db(svn_sqlite__db_t **sdb,
1537 apr_int64_t *repos_id,
1539 const char *dir_abspath,
1540 const char *repos_root_url,
1541 const char *repos_uuid,
1542 const char *sdb_fname,
1543 const char *root_node_repos_relpath,
1544 svn_revnum_t root_node_revision,
1545 svn_depth_t root_node_depth,
1546 svn_boolean_t exclusive,
1547 apr_pool_t *result_pool,
1548 apr_pool_t *scratch_pool)
1550 SVN_ERR(svn_wc__db_util_open_db(sdb, dir_abspath, sdb_fname,
1551 svn_sqlite__mode_rwcreate, exclusive,
1552 NULL /* my_statements */,
1553 result_pool, scratch_pool));
1555 SVN_SQLITE__WITH_LOCK(init_db(repos_id, wc_id,
1556 *sdb, repos_root_url, repos_uuid,
1557 root_node_repos_relpath, root_node_revision,
1558 root_node_depth, scratch_pool),
1561 return SVN_NO_ERROR;
1566 svn_wc__db_init(svn_wc__db_t *db,
1567 const char *local_abspath,
1568 const char *repos_relpath,
1569 const char *repos_root_url,
1570 const char *repos_uuid,
1571 svn_revnum_t initial_rev,
1573 apr_pool_t *scratch_pool)
1575 svn_sqlite__db_t *sdb;
1576 apr_int64_t repos_id;
1578 svn_wc__db_wcroot_t *wcroot;
1579 svn_boolean_t sqlite_exclusive = FALSE;
1581 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1582 SVN_ERR_ASSERT(repos_relpath != NULL);
1583 SVN_ERR_ASSERT(depth == svn_depth_empty
1584 || depth == svn_depth_files
1585 || depth == svn_depth_immediates
1586 || depth == svn_depth_infinity);
1588 /* ### REPOS_ROOT_URL and REPOS_UUID may be NULL. ... more doc: tbd */
1590 SVN_ERR(svn_config_get_bool((svn_config_t *)db->config, &sqlite_exclusive,
1591 SVN_CONFIG_SECTION_WORKING_COPY,
1592 SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
1595 /* Create the SDB and insert the basic rows. */
1596 SVN_ERR(create_db(&sdb, &repos_id, &wc_id, local_abspath, repos_root_url,
1597 repos_uuid, SDB_FILE,
1598 repos_relpath, initial_rev, depth, sqlite_exclusive,
1599 db->state_pool, scratch_pool));
1601 /* Create the WCROOT for this directory. */
1602 SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
1603 apr_pstrdup(db->state_pool, local_abspath),
1604 sdb, wc_id, FORMAT_FROM_SDB,
1605 FALSE /* auto-upgrade */,
1606 FALSE /* enforce_empty_wq */,
1607 db->state_pool, scratch_pool));
1609 /* The WCROOT is complete. Stash it into DB. */
1610 svn_hash_sets(db->dir_data, wcroot->abspath, wcroot);
1612 return SVN_NO_ERROR;
1617 svn_wc__db_to_relpath(const char **local_relpath,
1619 const char *wri_abspath,
1620 const char *local_abspath,
1621 apr_pool_t *result_pool,
1622 apr_pool_t *scratch_pool)
1624 svn_wc__db_wcroot_t *wcroot;
1625 const char *relpath;
1627 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1629 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &relpath, db,
1630 wri_abspath, result_pool, scratch_pool));
1632 /* This function is indirectly called from the upgrade code, so we
1633 can't verify the wcroot here. Just check that it is not NULL */
1634 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1636 if (svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
1638 *local_relpath = apr_pstrdup(result_pool,
1639 svn_dirent_skip_ancestor(wcroot->abspath,
1643 /* Probably moving from $TMP. Should we allow this? */
1644 *local_relpath = apr_pstrdup(result_pool, local_abspath);
1646 return SVN_NO_ERROR;
1651 svn_wc__db_from_relpath(const char **local_abspath,
1653 const char *wri_abspath,
1654 const char *local_relpath,
1655 apr_pool_t *result_pool,
1656 apr_pool_t *scratch_pool)
1658 svn_wc__db_wcroot_t *wcroot;
1659 const char *unused_relpath;
1661 SVN_ERR_ASSERT(svn_relpath_is_canonical(local_relpath));
1664 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1665 wri_abspath, scratch_pool, scratch_pool));
1667 /* This function is indirectly called from the upgrade code, so we
1668 can't verify the wcroot here. Just check that it is not NULL */
1669 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1672 *local_abspath = svn_dirent_join(wcroot->abspath,
1675 return SVN_NO_ERROR;
1680 svn_wc__db_get_wcroot(const char **wcroot_abspath,
1682 const char *wri_abspath,
1683 apr_pool_t *result_pool,
1684 apr_pool_t *scratch_pool)
1686 svn_wc__db_wcroot_t *wcroot;
1687 const char *unused_relpath;
1689 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1690 wri_abspath, scratch_pool, scratch_pool));
1692 /* Can't use VERIFY_USABLE_WCROOT, as this should be usable to detect
1693 where call upgrade */
1694 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1696 *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
1698 return SVN_NO_ERROR;
1703 svn_wc__db_base_add_directory(svn_wc__db_t *db,
1704 const char *local_abspath,
1705 const char *wri_abspath,
1706 const char *repos_relpath,
1707 const char *repos_root_url,
1708 const char *repos_uuid,
1709 svn_revnum_t revision,
1710 const apr_hash_t *props,
1711 svn_revnum_t changed_rev,
1712 apr_time_t changed_date,
1713 const char *changed_author,
1714 const apr_array_header_t *children,
1716 apr_hash_t *dav_cache,
1717 const svn_skel_t *conflict,
1718 svn_boolean_t update_actual_props,
1719 apr_hash_t *new_actual_props,
1720 apr_array_header_t *new_iprops,
1721 const svn_skel_t *work_items,
1722 apr_pool_t *scratch_pool)
1724 svn_wc__db_wcroot_t *wcroot;
1725 const char *local_relpath;
1726 insert_base_baton_t ibb;
1728 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1729 SVN_ERR_ASSERT(repos_relpath != NULL);
1730 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1731 SVN_ERR_ASSERT(repos_uuid != NULL);
1732 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1733 SVN_ERR_ASSERT(props != NULL);
1734 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1736 SVN_ERR_ASSERT(children != NULL);
1739 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1740 wri_abspath, scratch_pool, scratch_pool));
1741 VERIFY_USABLE_WCROOT(wcroot);
1742 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1746 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1747 ibb.repos_root_url = repos_root_url;
1748 ibb.repos_uuid = repos_uuid;
1750 ibb.status = svn_wc__db_status_normal;
1751 ibb.kind = svn_node_dir;
1752 ibb.repos_relpath = repos_relpath;
1753 ibb.revision = revision;
1755 ibb.iprops = new_iprops;
1757 ibb.changed_rev = changed_rev;
1758 ibb.changed_date = changed_date;
1759 ibb.changed_author = changed_author;
1761 ibb.children = children;
1764 ibb.dav_cache = dav_cache;
1765 ibb.conflict = conflict;
1766 ibb.work_items = work_items;
1768 if (update_actual_props)
1770 ibb.update_actual_props = TRUE;
1771 ibb.new_actual_props = new_actual_props;
1774 /* Insert the directory and all its children transactionally.
1776 Note: old children can stick around, even if they are no longer present
1777 in this directory's revision. */
1778 SVN_WC__DB_WITH_TXN(
1779 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1782 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
1783 return SVN_NO_ERROR;
1787 svn_wc__db_base_add_incomplete_directory(svn_wc__db_t *db,
1788 const char *local_abspath,
1789 const char *repos_relpath,
1790 const char *repos_root_url,
1791 const char *repos_uuid,
1792 svn_revnum_t revision,
1794 svn_boolean_t insert_base_deleted,
1795 svn_boolean_t delete_working,
1796 svn_skel_t *conflict,
1797 svn_skel_t *work_items,
1798 apr_pool_t *scratch_pool)
1800 svn_wc__db_wcroot_t *wcroot;
1801 const char *local_relpath;
1802 struct insert_base_baton_t ibb;
1804 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1805 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1806 SVN_ERR_ASSERT(repos_relpath && repos_root_url && repos_uuid);
1808 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
1810 scratch_pool, scratch_pool));
1812 VERIFY_USABLE_WCROOT(wcroot);
1816 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1817 ibb.repos_root_url = repos_root_url;
1818 ibb.repos_uuid = repos_uuid;
1820 ibb.status = svn_wc__db_status_incomplete;
1821 ibb.kind = svn_node_dir;
1822 ibb.repos_relpath = repos_relpath;
1823 ibb.revision = revision;
1825 ibb.insert_base_deleted = insert_base_deleted;
1826 ibb.delete_working = delete_working;
1828 ibb.conflict = conflict;
1829 ibb.work_items = work_items;
1831 SVN_WC__DB_WITH_TXN(
1832 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1835 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
1837 return SVN_NO_ERROR;
1842 svn_wc__db_base_add_file(svn_wc__db_t *db,
1843 const char *local_abspath,
1844 const char *wri_abspath,
1845 const char *repos_relpath,
1846 const char *repos_root_url,
1847 const char *repos_uuid,
1848 svn_revnum_t revision,
1849 const apr_hash_t *props,
1850 svn_revnum_t changed_rev,
1851 apr_time_t changed_date,
1852 const char *changed_author,
1853 const svn_checksum_t *checksum,
1854 apr_hash_t *dav_cache,
1855 svn_boolean_t delete_working,
1856 svn_boolean_t update_actual_props,
1857 apr_hash_t *new_actual_props,
1858 apr_array_header_t *new_iprops,
1859 svn_boolean_t keep_recorded_info,
1860 svn_boolean_t insert_base_deleted,
1861 const svn_skel_t *conflict,
1862 const svn_skel_t *work_items,
1863 apr_pool_t *scratch_pool)
1865 svn_wc__db_wcroot_t *wcroot;
1866 const char *local_relpath;
1867 insert_base_baton_t ibb;
1869 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1870 SVN_ERR_ASSERT(repos_relpath != NULL);
1871 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1872 SVN_ERR_ASSERT(repos_uuid != NULL);
1873 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1874 SVN_ERR_ASSERT(props != NULL);
1875 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1876 SVN_ERR_ASSERT(checksum != NULL);
1878 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1879 wri_abspath, scratch_pool, scratch_pool));
1880 VERIFY_USABLE_WCROOT(wcroot);
1881 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1885 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1886 ibb.repos_root_url = repos_root_url;
1887 ibb.repos_uuid = repos_uuid;
1889 ibb.status = svn_wc__db_status_normal;
1890 ibb.kind = svn_node_file;
1891 ibb.repos_relpath = repos_relpath;
1892 ibb.revision = revision;
1895 ibb.changed_rev = changed_rev;
1896 ibb.changed_date = changed_date;
1897 ibb.changed_author = changed_author;
1899 ibb.checksum = checksum;
1901 ibb.dav_cache = dav_cache;
1902 ibb.iprops = new_iprops;
1904 if (update_actual_props)
1906 ibb.update_actual_props = TRUE;
1907 ibb.new_actual_props = new_actual_props;
1910 ibb.keep_recorded_info = keep_recorded_info;
1911 ibb.insert_base_deleted = insert_base_deleted;
1912 ibb.delete_working = delete_working;
1914 ibb.conflict = conflict;
1915 ibb.work_items = work_items;
1917 SVN_WC__DB_WITH_TXN(
1918 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1921 /* If this used to be a directory we should remove children so pass
1922 * depth infinity. */
1923 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1925 return SVN_NO_ERROR;
1930 svn_wc__db_base_add_symlink(svn_wc__db_t *db,
1931 const char *local_abspath,
1932 const char *wri_abspath,
1933 const char *repos_relpath,
1934 const char *repos_root_url,
1935 const char *repos_uuid,
1936 svn_revnum_t revision,
1937 const apr_hash_t *props,
1938 svn_revnum_t changed_rev,
1939 apr_time_t changed_date,
1940 const char *changed_author,
1942 apr_hash_t *dav_cache,
1943 svn_boolean_t delete_working,
1944 svn_boolean_t update_actual_props,
1945 apr_hash_t *new_actual_props,
1946 apr_array_header_t *new_iprops,
1947 svn_boolean_t keep_recorded_info,
1948 svn_boolean_t insert_base_deleted,
1949 const svn_skel_t *conflict,
1950 const svn_skel_t *work_items,
1951 apr_pool_t *scratch_pool)
1953 svn_wc__db_wcroot_t *wcroot;
1954 const char *local_relpath;
1955 insert_base_baton_t ibb;
1957 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1958 SVN_ERR_ASSERT(repos_relpath != NULL);
1959 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1960 SVN_ERR_ASSERT(repos_uuid != NULL);
1961 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1962 SVN_ERR_ASSERT(props != NULL);
1963 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1964 SVN_ERR_ASSERT(target != NULL);
1966 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1967 wri_abspath, scratch_pool, scratch_pool));
1968 VERIFY_USABLE_WCROOT(wcroot);
1969 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1972 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1973 ibb.repos_root_url = repos_root_url;
1974 ibb.repos_uuid = repos_uuid;
1976 ibb.status = svn_wc__db_status_normal;
1977 ibb.kind = svn_node_symlink;
1978 ibb.repos_relpath = repos_relpath;
1979 ibb.revision = revision;
1982 ibb.changed_rev = changed_rev;
1983 ibb.changed_date = changed_date;
1984 ibb.changed_author = changed_author;
1986 ibb.target = target;
1988 ibb.dav_cache = dav_cache;
1989 ibb.iprops = new_iprops;
1991 if (update_actual_props)
1993 ibb.update_actual_props = TRUE;
1994 ibb.new_actual_props = new_actual_props;
1997 ibb.keep_recorded_info = keep_recorded_info;
1998 ibb.insert_base_deleted = insert_base_deleted;
1999 ibb.delete_working = delete_working;
2001 ibb.conflict = conflict;
2002 ibb.work_items = work_items;
2004 SVN_WC__DB_WITH_TXN(
2005 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
2008 /* If this used to be a directory we should remove children so pass
2009 * depth infinity. */
2010 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2012 return SVN_NO_ERROR;
2016 static svn_error_t *
2017 add_excluded_or_not_present_node(svn_wc__db_t *db,
2018 const char *local_abspath,
2019 const char *repos_relpath,
2020 const char *repos_root_url,
2021 const char *repos_uuid,
2022 svn_revnum_t revision,
2023 svn_node_kind_t kind,
2024 svn_wc__db_status_t status,
2025 const svn_skel_t *conflict,
2026 const svn_skel_t *work_items,
2027 apr_pool_t *scratch_pool)
2029 svn_wc__db_wcroot_t *wcroot;
2030 const char *local_relpath;
2031 insert_base_baton_t ibb;
2032 const char *dir_abspath, *name;
2034 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2035 SVN_ERR_ASSERT(repos_relpath != NULL);
2036 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
2037 SVN_ERR_ASSERT(repos_uuid != NULL);
2038 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
2039 SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
2040 || status == svn_wc__db_status_excluded
2041 || status == svn_wc__db_status_not_present);
2043 /* These absent presence nodes are only useful below a parent node that is
2044 present. To avoid problems with working copies obstructing the child
2045 we calculate the wcroot and local_relpath of the parent and then add
2048 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
2050 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2051 dir_abspath, scratch_pool, scratch_pool));
2052 VERIFY_USABLE_WCROOT(wcroot);
2054 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
2058 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
2059 ibb.repos_root_url = repos_root_url;
2060 ibb.repos_uuid = repos_uuid;
2062 ibb.status = status;
2064 ibb.repos_relpath = repos_relpath;
2065 ibb.revision = revision;
2067 /* Depending upon KIND, any of these might get used. */
2068 ibb.children = NULL;
2069 ibb.depth = svn_depth_unknown;
2070 ibb.checksum = NULL;
2073 ibb.conflict = conflict;
2074 ibb.work_items = work_items;
2076 SVN_WC__DB_WITH_TXN(
2077 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
2080 /* If this used to be a directory we should remove children so pass
2081 * depth infinity. */
2082 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2085 return SVN_NO_ERROR;
2090 svn_wc__db_base_add_excluded_node(svn_wc__db_t *db,
2091 const char *local_abspath,
2092 const char *repos_relpath,
2093 const char *repos_root_url,
2094 const char *repos_uuid,
2095 svn_revnum_t revision,
2096 svn_node_kind_t kind,
2097 svn_wc__db_status_t status,
2098 const svn_skel_t *conflict,
2099 const svn_skel_t *work_items,
2100 apr_pool_t *scratch_pool)
2102 SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
2103 || status == svn_wc__db_status_excluded);
2105 return add_excluded_or_not_present_node(
2106 db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2107 kind, status, conflict, work_items, scratch_pool);
2112 svn_wc__db_base_add_not_present_node(svn_wc__db_t *db,
2113 const char *local_abspath,
2114 const char *repos_relpath,
2115 const char *repos_root_url,
2116 const char *repos_uuid,
2117 svn_revnum_t revision,
2118 svn_node_kind_t kind,
2119 const svn_skel_t *conflict,
2120 const svn_skel_t *work_items,
2121 apr_pool_t *scratch_pool)
2123 return add_excluded_or_not_present_node(
2124 db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2125 kind, svn_wc__db_status_not_present, conflict, work_items, scratch_pool);
2128 /* Recursively clear moved-here information at the copy-half of the move
2129 * which moved the node at SRC_RELPATH away. This transforms the move into
2131 static svn_error_t *
2132 clear_moved_here(const char *src_relpath,
2133 svn_wc__db_wcroot_t *wcroot,
2134 apr_pool_t *scratch_pool)
2136 svn_sqlite__stmt_t *stmt;
2137 const char *dst_relpath;
2139 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
2140 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2141 src_relpath, relpath_depth(src_relpath)));
2142 SVN_ERR(svn_sqlite__step_row(stmt));
2143 dst_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
2144 SVN_ERR(svn_sqlite__reset(stmt));
2146 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2147 STMT_CLEAR_MOVED_HERE_RECURSIVE));
2148 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2149 dst_relpath, relpath_depth(dst_relpath)));
2150 SVN_ERR(svn_sqlite__step_done(stmt));
2152 return SVN_NO_ERROR;
2155 /* The body of svn_wc__db_base_remove().
2157 static svn_error_t *
2158 db_base_remove(svn_wc__db_wcroot_t *wcroot,
2159 const char *local_relpath,
2160 svn_wc__db_t *db, /* For checking conflicts */
2161 svn_boolean_t keep_as_working,
2162 svn_boolean_t queue_deletes,
2163 svn_boolean_t remove_locks,
2164 svn_revnum_t not_present_revision,
2165 svn_skel_t *conflict,
2166 svn_skel_t *work_items,
2167 apr_pool_t *scratch_pool)
2169 svn_sqlite__stmt_t *stmt;
2170 svn_boolean_t have_row;
2171 svn_wc__db_status_t status;
2172 apr_int64_t repos_id;
2173 const char *repos_relpath;
2174 svn_node_kind_t kind;
2175 svn_boolean_t keep_working;
2177 SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, NULL,
2178 &repos_relpath, &repos_id,
2179 NULL, NULL, NULL, NULL, NULL,
2180 NULL, NULL, NULL, NULL, NULL,
2181 wcroot, local_relpath,
2182 scratch_pool, scratch_pool));
2186 svn_sqlite__stmt_t *lock_stmt;
2188 SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
2189 STMT_DELETE_LOCK_RECURSIVELY));
2190 SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
2191 SVN_ERR(svn_sqlite__step_done(lock_stmt));
2194 if (status == svn_wc__db_status_normal
2197 SVN_ERR(svn_wc__db_op_make_copy(db,
2198 svn_dirent_join(wcroot->abspath,
2203 keep_working = TRUE;
2207 /* Check if there is already a working node */
2208 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2209 STMT_SELECT_WORKING_NODE));
2210 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2211 SVN_ERR(svn_sqlite__step(&keep_working, stmt));
2212 SVN_ERR(svn_sqlite__reset(stmt));
2215 /* Step 1: Create workqueue operations to remove files and dirs in the
2219 && (status == svn_wc__db_status_normal
2220 || status == svn_wc__db_status_incomplete))
2222 svn_skel_t *work_item;
2223 const char *local_abspath;
2225 local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
2227 if (kind == svn_node_dir)
2229 apr_pool_t *iterpool;
2230 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2231 STMT_SELECT_BASE_PRESENT));
2232 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2234 iterpool = svn_pool_create(scratch_pool);
2236 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2240 const char *node_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2241 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 1,
2243 const char *node_abspath;
2246 svn_pool_clear(iterpool);
2248 node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
2251 if (node_kind == svn_node_dir)
2252 err = svn_wc__wq_build_dir_remove(&work_item,
2253 db, wcroot->abspath,
2254 node_abspath, FALSE,
2255 iterpool, iterpool);
2257 err = svn_wc__wq_build_file_remove(&work_item,
2261 iterpool, iterpool);
2264 err = add_work_items(wcroot->sdb, work_item, iterpool);
2266 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2268 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2271 SVN_ERR(svn_sqlite__reset(stmt));
2273 SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
2274 db, wcroot->abspath,
2275 local_abspath, FALSE,
2276 scratch_pool, iterpool));
2277 svn_pool_destroy(iterpool);
2280 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
2281 db, wcroot->abspath,
2283 scratch_pool, scratch_pool));
2285 SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
2288 /* Step 2: Delete ACTUAL nodes */
2291 /* There won't be a record in NODE left for this node, so we want
2292 to remove *all* ACTUAL nodes, including ACTUAL ONLY. */
2293 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2294 STMT_DELETE_ACTUAL_NODE_RECURSIVE));
2295 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2296 SVN_ERR(svn_sqlite__step_done(stmt));
2298 else if (! keep_as_working)
2300 /* Delete only the ACTUAL nodes that apply to a delete of a BASE node */
2301 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2302 STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE));
2303 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2304 SVN_ERR(svn_sqlite__step_done(stmt));
2306 /* Else: Everything has been turned into a copy, so we want to keep all
2307 ACTUAL_NODE records */
2309 /* Step 3: Delete WORKING nodes */
2312 apr_pool_t *iterpool;
2315 * When deleting a conflicted node, moves of any moved-outside children
2316 * of the node must be broken. Else, the destination will still be marked
2317 * moved-here after the move source disappears from the working copy.
2319 * ### FIXME: It would be nicer to have the conflict resolver
2320 * break the move instead. It might also be a good idea to
2321 * flag a tree conflict on each moved-away child. But doing so
2322 * might introduce actual-only nodes without direct parents,
2323 * and we're not yet sure if other existing code is prepared
2324 * to handle such nodes. To be revisited post-1.8.
2326 * ### In case of a conflict we are most likely creating WORKING nodes
2327 * describing a copy of what was in BASE. The move information
2328 * should be updated to describe a move from the WORKING layer.
2329 * When stored that way the resolver of the tree conflict still has
2330 * the knowledge of what was moved.
2332 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2333 STMT_SELECT_MOVED_OUTSIDE));
2334 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2336 relpath_depth(local_relpath)));
2337 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2338 iterpool = svn_pool_create(scratch_pool);
2341 const char *child_relpath;
2344 svn_pool_clear(iterpool);
2345 child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
2346 err = clear_moved_here(child_relpath, wcroot, iterpool);
2348 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2349 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2351 svn_pool_destroy(iterpool);
2352 SVN_ERR(svn_sqlite__reset(stmt));
2356 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2357 STMT_DELETE_WORKING_BASE_DELETE));
2358 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2359 SVN_ERR(svn_sqlite__step_done(stmt));
2363 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2364 STMT_DELETE_WORKING_RECURSIVE));
2365 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2366 SVN_ERR(svn_sqlite__step_done(stmt));
2369 /* Step 4: Delete the BASE node descendants */
2370 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2371 STMT_DELETE_BASE_RECURSIVE));
2372 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2373 SVN_ERR(svn_sqlite__step_done(stmt));
2375 /* Step 5: handle the BASE node itself */
2376 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2377 STMT_DELETE_BASE_NODE));
2378 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2379 SVN_ERR(svn_sqlite__step_done(stmt));
2381 SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
2384 /* Step 6: Delete actual node if we don't keep working */
2387 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2388 STMT_DELETE_ACTUAL_NODE));
2389 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2390 SVN_ERR(svn_sqlite__step_done(stmt));
2393 if (SVN_IS_VALID_REVNUM(not_present_revision))
2395 struct insert_base_baton_t ibb;
2398 ibb.repos_id = repos_id;
2399 ibb.status = svn_wc__db_status_not_present;
2401 ibb.repos_relpath = repos_relpath;
2402 ibb.revision = not_present_revision;
2404 /* Depending upon KIND, any of these might get used. */
2405 ibb.children = NULL;
2406 ibb.depth = svn_depth_unknown;
2407 ibb.checksum = NULL;
2410 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
2413 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2415 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
2416 conflict, scratch_pool));
2418 return SVN_NO_ERROR;
2423 svn_wc__db_base_remove(svn_wc__db_t *db,
2424 const char *local_abspath,
2425 svn_boolean_t keep_as_working,
2426 svn_boolean_t queue_deletes,
2427 svn_boolean_t remove_locks,
2428 svn_revnum_t not_present_revision,
2429 svn_skel_t *conflict,
2430 svn_skel_t *work_items,
2431 apr_pool_t *scratch_pool)
2433 svn_wc__db_wcroot_t *wcroot;
2434 const char *local_relpath;
2436 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2438 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2439 local_abspath, scratch_pool, scratch_pool));
2440 VERIFY_USABLE_WCROOT(wcroot);
2442 SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath,
2443 db, keep_as_working, queue_deletes,
2444 remove_locks, not_present_revision,
2445 conflict, work_items, scratch_pool),
2448 /* If this used to be a directory we should remove children so pass
2449 * depth infinity. */
2450 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2453 return SVN_NO_ERROR;
2458 svn_wc__db_base_get_info_internal(svn_wc__db_status_t *status,
2459 svn_node_kind_t *kind,
2460 svn_revnum_t *revision,
2461 const char **repos_relpath,
2462 apr_int64_t *repos_id,
2463 svn_revnum_t *changed_rev,
2464 apr_time_t *changed_date,
2465 const char **changed_author,
2467 const svn_checksum_t **checksum,
2468 const char **target,
2469 svn_wc__db_lock_t **lock,
2470 svn_boolean_t *had_props,
2472 svn_boolean_t *update_root,
2473 svn_wc__db_wcroot_t *wcroot,
2474 const char *local_relpath,
2475 apr_pool_t *result_pool,
2476 apr_pool_t *scratch_pool)
2478 svn_sqlite__stmt_t *stmt;
2479 svn_boolean_t have_row;
2480 svn_error_t *err = SVN_NO_ERROR;
2482 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2483 lock ? STMT_SELECT_BASE_NODE_WITH_LOCK
2484 : STMT_SELECT_BASE_NODE));
2485 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2486 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2490 svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2492 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2500 *status = node_status;
2502 repos_location_from_columns(repos_id, revision, repos_relpath,
2503 stmt, 0, 4, 1, result_pool);
2504 SVN_ERR_ASSERT(!repos_id || *repos_id != INVALID_REPOS_ID);
2505 SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
2508 *lock = lock_from_columns(stmt, 15, 16, 17, 18, result_pool);
2512 *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2516 *changed_date = svn_sqlite__column_int64(stmt, 8);
2520 /* Result may be NULL. */
2521 *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2525 if (node_kind != svn_node_dir)
2527 *depth = svn_depth_unknown;
2531 *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2537 if (node_kind != svn_node_file)
2543 err = svn_sqlite__column_checksum(checksum, stmt, 5,
2546 err = svn_error_createf(
2548 _("The node '%s' has a corrupt checksum value."),
2549 path_for_error_message(wcroot, local_relpath,
2555 if (node_kind != svn_node_symlink)
2558 *target = svn_sqlite__column_text(stmt, 11, result_pool);
2562 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2566 if (node_status == svn_wc__db_status_normal
2567 || node_status == svn_wc__db_status_incomplete)
2569 SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2570 result_pool, scratch_pool));
2572 *props = apr_hash_make(result_pool);
2576 assert(svn_sqlite__column_is_null(stmt, 13));
2582 /* It's an update root iff it's a file external. */
2583 *update_root = svn_sqlite__column_boolean(stmt, 14);
2588 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2589 _("The node '%s' was not found."),
2590 path_for_error_message(wcroot, local_relpath,
2594 /* Note: given the composition, no need to wrap for tracing. */
2595 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2600 svn_wc__db_base_get_info(svn_wc__db_status_t *status,
2601 svn_node_kind_t *kind,
2602 svn_revnum_t *revision,
2603 const char **repos_relpath,
2604 const char **repos_root_url,
2605 const char **repos_uuid,
2606 svn_revnum_t *changed_rev,
2607 apr_time_t *changed_date,
2608 const char **changed_author,
2610 const svn_checksum_t **checksum,
2611 const char **target,
2612 svn_wc__db_lock_t **lock,
2613 svn_boolean_t *had_props,
2615 svn_boolean_t *update_root,
2617 const char *local_abspath,
2618 apr_pool_t *result_pool,
2619 apr_pool_t *scratch_pool)
2621 svn_wc__db_wcroot_t *wcroot;
2622 const char *local_relpath;
2623 apr_int64_t repos_id;
2625 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2627 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2628 local_abspath, scratch_pool, scratch_pool));
2629 VERIFY_USABLE_WCROOT(wcroot);
2631 SVN_WC__DB_WITH_TXN4(
2632 svn_wc__db_base_get_info_internal(status, kind, revision,
2633 repos_relpath, &repos_id,
2634 changed_rev, changed_date,
2635 changed_author, depth,
2636 checksum, target, lock,
2637 had_props, props, update_root,
2638 wcroot, local_relpath,
2639 result_pool, scratch_pool),
2640 svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
2641 wcroot->sdb, repos_id, result_pool),
2645 SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
2647 return SVN_NO_ERROR;
2651 svn_wc__db_base_get_children_info(apr_hash_t **nodes,
2653 const char *dir_abspath,
2654 apr_pool_t *result_pool,
2655 apr_pool_t *scratch_pool)
2657 svn_wc__db_wcroot_t *wcroot;
2658 const char *local_relpath;
2659 svn_sqlite__stmt_t *stmt;
2660 svn_boolean_t have_row;
2662 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
2664 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2665 dir_abspath, scratch_pool, scratch_pool));
2666 VERIFY_USABLE_WCROOT(wcroot);
2668 *nodes = apr_hash_make(result_pool);
2670 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2671 STMT_SELECT_BASE_CHILDREN_INFO));
2672 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2674 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2678 struct svn_wc__db_base_info_t *info;
2680 apr_int64_t repos_id;
2681 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2682 const char *name = svn_relpath_basename(child_relpath, result_pool);
2684 info = apr_pcalloc(result_pool, sizeof(*info));
2686 repos_id = svn_sqlite__column_int64(stmt, 1);
2687 info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
2688 info->status = svn_sqlite__column_token(stmt, 3, presence_map);
2689 info->kind = svn_sqlite__column_token(stmt, 4, kind_map);
2690 info->revnum = svn_sqlite__column_revnum(stmt, 5);
2692 info->depth = svn_sqlite__column_token_null(stmt, 6, depth_map,
2695 info->update_root = svn_sqlite__column_boolean(stmt, 7);
2697 info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool);
2699 err = svn_wc__db_fetch_repos_info(&info->repos_root_url, NULL,
2700 wcroot->sdb, repos_id, result_pool);
2703 return svn_error_trace(
2704 svn_error_compose_create(err,
2705 svn_sqlite__reset(stmt)));
2708 svn_hash_sets(*nodes, name, info);
2710 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2713 SVN_ERR(svn_sqlite__reset(stmt));
2715 return SVN_NO_ERROR;
2720 svn_wc__db_base_get_props(apr_hash_t **props,
2722 const char *local_abspath,
2723 apr_pool_t *result_pool,
2724 apr_pool_t *scratch_pool)
2726 svn_wc__db_status_t presence;
2728 SVN_ERR(svn_wc__db_base_get_info(&presence, NULL, NULL, NULL, NULL,
2729 NULL, NULL, NULL, NULL, NULL,
2730 NULL, NULL, NULL, NULL, props, NULL,
2732 result_pool, scratch_pool));
2733 if (presence != svn_wc__db_status_normal
2734 && presence != svn_wc__db_status_incomplete)
2736 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2737 _("The node '%s' has a BASE status that"
2738 " has no properties."),
2739 svn_dirent_local_style(local_abspath,
2743 return SVN_NO_ERROR;
2748 svn_wc__db_base_get_children(const apr_array_header_t **children,
2750 const char *local_abspath,
2751 apr_pool_t *result_pool,
2752 apr_pool_t *scratch_pool)
2754 svn_wc__db_wcroot_t *wcroot;
2755 const char *local_relpath;
2757 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2759 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2761 scratch_pool, scratch_pool));
2762 VERIFY_USABLE_WCROOT(wcroot);
2764 return gather_repo_children(children, wcroot, local_relpath, 0,
2765 result_pool, scratch_pool);
2770 svn_wc__db_base_set_dav_cache(svn_wc__db_t *db,
2771 const char *local_abspath,
2772 const apr_hash_t *props,
2773 apr_pool_t *scratch_pool)
2775 svn_sqlite__stmt_t *stmt;
2778 SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
2779 STMT_UPDATE_BASE_NODE_DAV_CACHE,
2781 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
2783 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2785 if (affected_rows != 1)
2786 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2787 _("The node '%s' was not found."),
2788 svn_dirent_local_style(local_abspath,
2791 return SVN_NO_ERROR;
2796 svn_wc__db_base_get_dav_cache(apr_hash_t **props,
2798 const char *local_abspath,
2799 apr_pool_t *result_pool,
2800 apr_pool_t *scratch_pool)
2802 svn_sqlite__stmt_t *stmt;
2803 svn_boolean_t have_row;
2805 SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
2806 STMT_SELECT_BASE_DAV_CACHE, scratch_pool));
2807 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2809 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
2810 svn_sqlite__reset(stmt),
2811 _("The node '%s' was not found."),
2812 svn_dirent_local_style(local_abspath,
2815 SVN_ERR(svn_sqlite__column_properties(props, stmt, 0, result_pool,
2817 return svn_error_trace(svn_sqlite__reset(stmt));
2822 svn_wc__db_base_clear_dav_cache_recursive(svn_wc__db_t *db,
2823 const char *local_abspath,
2824 apr_pool_t *scratch_pool)
2826 svn_wc__db_wcroot_t *wcroot;
2827 const char *local_relpath;
2828 svn_sqlite__stmt_t *stmt;
2830 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2832 scratch_pool, scratch_pool));
2833 VERIFY_USABLE_WCROOT(wcroot);
2835 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2836 STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE));
2837 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2839 SVN_ERR(svn_sqlite__step_done(stmt));
2841 return SVN_NO_ERROR;
2846 svn_wc__db_depth_get_info(svn_wc__db_status_t *status,
2847 svn_node_kind_t *kind,
2848 svn_revnum_t *revision,
2849 const char **repos_relpath,
2850 apr_int64_t *repos_id,
2851 svn_revnum_t *changed_rev,
2852 apr_time_t *changed_date,
2853 const char **changed_author,
2855 const svn_checksum_t **checksum,
2856 const char **target,
2857 svn_boolean_t *had_props,
2859 svn_wc__db_wcroot_t *wcroot,
2860 const char *local_relpath,
2862 apr_pool_t *result_pool,
2863 apr_pool_t *scratch_pool)
2865 svn_sqlite__stmt_t *stmt;
2866 svn_boolean_t have_row;
2867 svn_error_t *err = SVN_NO_ERROR;
2869 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2870 STMT_SELECT_DEPTH_NODE));
2871 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
2872 wcroot->wc_id, local_relpath, op_depth));
2873 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2877 svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2879 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2887 *status = node_status;
2890 SVN_ERR(convert_to_working_status(status, *status));
2892 repos_location_from_columns(repos_id, revision, repos_relpath,
2893 stmt, 0, 4, 1, result_pool);
2897 *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2901 *changed_date = svn_sqlite__column_int64(stmt, 8);
2905 /* Result may be NULL. */
2906 *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2910 if (node_kind != svn_node_dir)
2912 *depth = svn_depth_unknown;
2916 *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2922 if (node_kind != svn_node_file)
2928 err = svn_sqlite__column_checksum(checksum, stmt, 5,
2931 err = svn_error_createf(
2933 _("The node '%s' has a corrupt checksum value."),
2934 path_for_error_message(wcroot, local_relpath,
2940 if (node_kind != svn_node_symlink)
2943 *target = svn_sqlite__column_text(stmt, 11, result_pool);
2947 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2951 if (node_status == svn_wc__db_status_normal
2952 || node_status == svn_wc__db_status_incomplete)
2954 SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2955 result_pool, scratch_pool));
2957 *props = apr_hash_make(result_pool);
2961 assert(svn_sqlite__column_is_null(stmt, 13));
2968 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2969 _("The node '%s' was not found."),
2970 path_for_error_message(wcroot, local_relpath,
2974 /* Note: given the composition, no need to wrap for tracing. */
2975 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2979 /* Baton for passing args to with_triggers(). */
2980 struct with_triggers_baton_t {
2983 svn_wc__db_txn_callback_t cb_func;
2987 /* Helper for creating SQLite triggers, running the main transaction
2988 callback, and then dropping the triggers. It guarantees that the
2989 triggers will not survive the transaction. This could be used for
2990 any general prefix/postscript statements where the postscript
2991 *must* be executed if the transaction completes.
2993 Implements svn_wc__db_txn_callback_t. */
2994 static svn_error_t *
2995 with_triggers(void *baton,
2996 svn_wc__db_wcroot_t *wcroot,
2997 const char *local_relpath,
2998 apr_pool_t *scratch_pool)
3000 struct with_triggers_baton_t *b = baton;
3004 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, b->create_trigger));
3006 err1 = b->cb_func(b->cb_baton, wcroot, local_relpath, scratch_pool);
3008 err2 = svn_sqlite__exec_statements(wcroot->sdb, b->drop_trigger);
3010 return svn_error_trace(svn_error_compose_create(err1, err2));
3014 /* Prototype for the "work callback" used by with_finalization(). */
3015 typedef svn_error_t * (*work_callback_t)(
3017 svn_wc__db_wcroot_t *wcroot,
3018 svn_cancel_func_t cancel_func,
3020 svn_wc_notify_func2_t notify_func,
3022 apr_pool_t *scratch_pool);
3024 /* Utility function to provide several features, with a guaranteed
3025 finalization (ie. to drop temporary tables).
3027 1) for WCROOT and LOCAL_RELPATH, run TXN_CB(TXN_BATON) within a
3029 2) if (1) is successful and a NOTIFY_FUNC is provided, then run
3030 the "work" step: WORK_CB(WORK_BATON).
3031 3) execute FINALIZE_STMT_IDX no matter what errors may be thrown
3032 from the above two steps.
3034 CANCEL_FUNC, CANCEL_BATON, NOTIFY_FUNC and NOTIFY_BATON are their
3035 typical values. These are passed to the work callback, which typically
3036 provides notification about the work done by TXN_CB. */
3037 static svn_error_t *
3038 with_finalization(svn_wc__db_wcroot_t *wcroot,
3039 const char *local_relpath,
3040 svn_wc__db_txn_callback_t txn_cb,
3042 work_callback_t work_cb,
3044 svn_cancel_func_t cancel_func,
3046 svn_wc_notify_func2_t notify_func,
3048 int finalize_stmt_idx,
3049 apr_pool_t *scratch_pool)
3054 err1 = svn_wc__db_with_txn(wcroot, local_relpath, txn_cb, txn_baton,
3057 if (err1 == NULL && notify_func != NULL)
3059 err2 = work_cb(work_baton, wcroot,
3060 cancel_func, cancel_baton,
3061 notify_func, notify_baton,
3063 err1 = svn_error_compose_create(err1, err2);
3066 err2 = svn_sqlite__exec_statements(wcroot->sdb, finalize_stmt_idx);
3068 return svn_error_trace(svn_error_compose_create(err1, err2));
3072 /* Initialize the baton with appropriate "blank" values. This allows the
3073 insertion function to leave certain columns null. */
3075 blank_ieb(insert_external_baton_t *ieb)
3077 memset(ieb, 0, sizeof(*ieb));
3078 ieb->revision = SVN_INVALID_REVNUM;
3079 ieb->changed_rev = SVN_INVALID_REVNUM;
3080 ieb->repos_id = INVALID_REPOS_ID;
3082 ieb->recorded_peg_revision = SVN_INVALID_REVNUM;
3083 ieb->recorded_revision = SVN_INVALID_REVNUM;
3086 /* Insert the externals row represented by (insert_external_baton_t *) BATON.
3088 * Implements svn_wc__db_txn_callback_t. */
3089 static svn_error_t *
3090 insert_external_node(const insert_external_baton_t *ieb,
3091 svn_wc__db_wcroot_t *wcroot,
3092 const char *local_relpath,
3093 apr_pool_t *scratch_pool)
3095 svn_wc__db_status_t status;
3097 svn_boolean_t update_root;
3098 apr_int64_t repos_id;
3099 svn_sqlite__stmt_t *stmt;
3101 if (ieb->repos_id != INVALID_REPOS_ID)
3102 repos_id = ieb->repos_id;
3104 SVN_ERR(create_repos_id(&repos_id, ieb->repos_root_url, ieb->repos_uuid,
3105 wcroot->sdb, scratch_pool));
3107 /* And there must be no existing BASE node or it must be a file external */
3108 err = svn_wc__db_base_get_info_internal(&status, NULL, NULL, NULL, NULL,
3109 NULL, NULL, NULL, NULL, NULL,
3110 NULL, NULL, NULL, NULL, &update_root,
3111 wcroot, local_relpath,
3112 scratch_pool, scratch_pool);
3115 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3116 return svn_error_trace(err);
3118 svn_error_clear(err);
3120 else if (status == svn_wc__db_status_normal && !update_root)
3121 return svn_error_create(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, NULL);
3123 if (ieb->kind == svn_node_file
3124 || ieb->kind == svn_node_symlink)
3126 struct insert_base_baton_t ibb;
3130 ibb.status = svn_wc__db_status_normal;
3131 ibb.kind = ieb->kind;
3133 ibb.repos_id = repos_id;
3134 ibb.repos_relpath = ieb->repos_relpath;
3135 ibb.revision = ieb->revision;
3137 ibb.props = ieb->props;
3138 ibb.iprops = ieb->iprops;
3139 ibb.changed_rev = ieb->changed_rev;
3140 ibb.changed_date = ieb->changed_date;
3141 ibb.changed_author = ieb->changed_author;
3143 ibb.dav_cache = ieb->dav_cache;
3145 ibb.checksum = ieb->checksum;
3146 ibb.target = ieb->target;
3148 ibb.conflict = ieb->conflict;
3150 ibb.update_actual_props = ieb->update_actual_props;
3151 ibb.new_actual_props = ieb->new_actual_props;
3153 ibb.keep_recorded_info = ieb->keep_recorded_info;
3155 ibb.work_items = ieb->work_items;
3157 ibb.file_external = TRUE;
3159 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
3162 SVN_ERR(add_work_items(wcroot->sdb, ieb->work_items, scratch_pool));
3164 /* The externals table only support presence normal and excluded */
3165 SVN_ERR_ASSERT(ieb->presence == svn_wc__db_status_normal
3166 || ieb->presence == svn_wc__db_status_excluded);
3168 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_EXTERNAL));
3170 SVN_ERR(svn_sqlite__bindf(stmt, "issttsis",
3173 svn_relpath_dirname(local_relpath,
3175 presence_map, ieb->presence,
3176 kind_map, ieb->kind,
3177 ieb->record_ancestor_relpath,
3179 ieb->recorded_repos_relpath));
3181 if (SVN_IS_VALID_REVNUM(ieb->recorded_peg_revision))
3182 SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, ieb->recorded_peg_revision));
3184 if (SVN_IS_VALID_REVNUM(ieb->recorded_revision))
3185 SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, ieb->recorded_revision));
3187 SVN_ERR(svn_sqlite__insert(NULL, stmt));
3189 return SVN_NO_ERROR;
3193 svn_wc__db_external_add_file(svn_wc__db_t *db,
3194 const char *local_abspath,
3195 const char *wri_abspath,
3197 const char *repos_relpath,
3198 const char *repos_root_url,
3199 const char *repos_uuid,
3200 svn_revnum_t revision,
3202 const apr_hash_t *props,
3203 apr_array_header_t *iprops,
3205 svn_revnum_t changed_rev,
3206 apr_time_t changed_date,
3207 const char *changed_author,
3209 const svn_checksum_t *checksum,
3211 const apr_hash_t *dav_cache,
3213 const char *record_ancestor_abspath,
3214 const char *recorded_repos_relpath,
3215 svn_revnum_t recorded_peg_revision,
3216 svn_revnum_t recorded_revision,
3218 svn_boolean_t update_actual_props,
3219 apr_hash_t *new_actual_props,
3221 svn_boolean_t keep_recorded_info,
3222 const svn_skel_t *conflict,
3223 const svn_skel_t *work_items,
3224 apr_pool_t *scratch_pool)
3226 svn_wc__db_wcroot_t *wcroot;
3227 const char *local_relpath;
3228 insert_external_baton_t ieb;
3230 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3233 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3235 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3236 wri_abspath, scratch_pool, scratch_pool));
3237 VERIFY_USABLE_WCROOT(wcroot);
3239 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3240 record_ancestor_abspath));
3242 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3244 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3248 ieb.kind = svn_node_file;
3249 ieb.presence = svn_wc__db_status_normal;
3251 ieb.repos_root_url = repos_root_url;
3252 ieb.repos_uuid = repos_uuid;
3254 ieb.repos_relpath = repos_relpath;
3255 ieb.revision = revision;
3258 ieb.iprops = iprops;
3260 ieb.changed_rev = changed_rev;
3261 ieb.changed_date = changed_date;
3262 ieb.changed_author = changed_author;
3264 ieb.checksum = checksum;
3266 ieb.dav_cache = dav_cache;
3268 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3270 record_ancestor_abspath);
3271 ieb.recorded_repos_relpath = recorded_repos_relpath;
3272 ieb.recorded_peg_revision = recorded_peg_revision;
3273 ieb.recorded_revision = recorded_revision;
3275 ieb.update_actual_props = update_actual_props;
3276 ieb.new_actual_props = new_actual_props;
3278 ieb.keep_recorded_info = keep_recorded_info;
3280 ieb.conflict = conflict;
3281 ieb.work_items = work_items;
3283 SVN_WC__DB_WITH_TXN(
3284 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3287 return SVN_NO_ERROR;
3291 svn_wc__db_external_add_symlink(svn_wc__db_t *db,
3292 const char *local_abspath,
3293 const char *wri_abspath,
3294 const char *repos_relpath,
3295 const char *repos_root_url,
3296 const char *repos_uuid,
3297 svn_revnum_t revision,
3298 const apr_hash_t *props,
3299 svn_revnum_t changed_rev,
3300 apr_time_t changed_date,
3301 const char *changed_author,
3303 const apr_hash_t *dav_cache,
3304 const char *record_ancestor_abspath,
3305 const char *recorded_repos_relpath,
3306 svn_revnum_t recorded_peg_revision,
3307 svn_revnum_t recorded_revision,
3308 svn_boolean_t update_actual_props,
3309 apr_hash_t *new_actual_props,
3310 svn_boolean_t keep_recorded_info,
3311 const svn_skel_t *work_items,
3312 apr_pool_t *scratch_pool)
3314 svn_wc__db_wcroot_t *wcroot;
3315 const char *local_relpath;
3316 insert_external_baton_t ieb;
3318 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3321 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3323 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3324 wri_abspath, scratch_pool, scratch_pool));
3325 VERIFY_USABLE_WCROOT(wcroot);
3327 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3328 record_ancestor_abspath));
3330 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3332 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3336 ieb.kind = svn_node_symlink;
3337 ieb.presence = svn_wc__db_status_normal;
3339 ieb.repos_root_url = repos_root_url;
3340 ieb.repos_uuid = repos_uuid;
3342 ieb.repos_relpath = repos_relpath;
3343 ieb.revision = revision;
3347 ieb.changed_rev = changed_rev;
3348 ieb.changed_date = changed_date;
3349 ieb.changed_author = changed_author;
3351 ieb.target = target;
3353 ieb.dav_cache = dav_cache;
3355 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3357 record_ancestor_abspath);
3358 ieb.recorded_repos_relpath = recorded_repos_relpath;
3359 ieb.recorded_peg_revision = recorded_peg_revision;
3360 ieb.recorded_revision = recorded_revision;
3362 ieb.update_actual_props = update_actual_props;
3363 ieb.new_actual_props = new_actual_props;
3365 ieb.keep_recorded_info = keep_recorded_info;
3367 ieb.work_items = work_items;
3369 SVN_WC__DB_WITH_TXN(
3370 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3373 return SVN_NO_ERROR;
3377 svn_wc__db_external_add_dir(svn_wc__db_t *db,
3378 const char *local_abspath,
3379 const char *wri_abspath,
3380 const char *repos_root_url,
3381 const char *repos_uuid,
3382 const char *record_ancestor_abspath,
3383 const char *recorded_repos_relpath,
3384 svn_revnum_t recorded_peg_revision,
3385 svn_revnum_t recorded_revision,
3386 const svn_skel_t *work_items,
3387 apr_pool_t *scratch_pool)
3389 svn_wc__db_wcroot_t *wcroot;
3390 const char *local_relpath;
3391 insert_external_baton_t ieb;
3393 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3396 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3398 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3399 wri_abspath, scratch_pool, scratch_pool));
3400 VERIFY_USABLE_WCROOT(wcroot);
3402 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3403 record_ancestor_abspath));
3405 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3407 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3411 ieb.kind = svn_node_dir;
3412 ieb.presence = svn_wc__db_status_normal;
3414 ieb.repos_root_url = repos_root_url;
3415 ieb.repos_uuid = repos_uuid;
3417 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3419 record_ancestor_abspath);
3420 ieb.recorded_repos_relpath = recorded_repos_relpath;
3421 ieb.recorded_peg_revision = recorded_peg_revision;
3422 ieb.recorded_revision = recorded_revision;
3424 ieb.work_items = work_items;
3426 SVN_WC__DB_WITH_TXN(
3427 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3430 return SVN_NO_ERROR;
3433 /* The body of svn_wc__db_external_remove(). */
3434 static svn_error_t *
3435 db_external_remove(const svn_skel_t *work_items,
3436 svn_wc__db_wcroot_t *wcroot,
3437 const char *local_relpath,
3438 apr_pool_t *scratch_pool)
3440 svn_sqlite__stmt_t *stmt;
3442 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3443 STMT_DELETE_EXTERNAL));
3444 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3445 SVN_ERR(svn_sqlite__step_done(stmt));
3447 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
3449 /* ### What about actual? */
3450 return SVN_NO_ERROR;
3454 svn_wc__db_external_remove(svn_wc__db_t *db,
3455 const char *local_abspath,
3456 const char *wri_abspath,
3457 const svn_skel_t *work_items,
3458 apr_pool_t *scratch_pool)
3460 svn_wc__db_wcroot_t *wcroot;
3461 const char *local_relpath;
3463 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3466 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3468 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3469 wri_abspath, scratch_pool, scratch_pool));
3470 VERIFY_USABLE_WCROOT(wcroot);
3472 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3474 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3476 SVN_WC__DB_WITH_TXN(db_external_remove(work_items, wcroot, local_relpath,
3480 return SVN_NO_ERROR;
3484 svn_wc__db_external_read(svn_wc__db_status_t *status,
3485 svn_node_kind_t *kind,
3486 const char **definining_abspath,
3487 const char **repos_root_url,
3488 const char **repos_uuid,
3489 const char **recorded_repos_relpath,
3490 svn_revnum_t *recorded_peg_revision,
3491 svn_revnum_t *recorded_revision,
3493 const char *local_abspath,
3494 const char *wri_abspath,
3495 apr_pool_t *result_pool,
3496 apr_pool_t *scratch_pool)
3498 svn_wc__db_wcroot_t *wcroot;
3499 const char *local_relpath;
3500 svn_sqlite__stmt_t *stmt;
3501 svn_boolean_t have_info;
3502 svn_error_t *err = NULL;
3503 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3506 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3508 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3509 wri_abspath, scratch_pool, scratch_pool));
3510 VERIFY_USABLE_WCROOT(wcroot);
3512 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3514 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3516 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3517 STMT_SELECT_EXTERNAL_INFO));
3518 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3519 SVN_ERR(svn_sqlite__step(&have_info, stmt));
3524 *status = svn_sqlite__column_token(stmt, 0, presence_map);
3527 *kind = svn_sqlite__column_token(stmt, 1, kind_map);
3529 if (definining_abspath)
3531 const char *record_relpath = svn_sqlite__column_text(stmt, 2, NULL);
3533 *definining_abspath = svn_dirent_join(wcroot->abspath,
3534 record_relpath, result_pool);
3537 if (repos_root_url || repos_uuid)
3539 apr_int64_t repos_id;
3541 repos_id = svn_sqlite__column_int64(stmt, 3);
3543 err = svn_error_compose_create(
3545 svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
3546 wcroot->sdb, repos_id,
3550 if (recorded_repos_relpath)
3551 *recorded_repos_relpath = svn_sqlite__column_text(stmt, 4,
3554 if (recorded_peg_revision)
3555 *recorded_peg_revision = svn_sqlite__column_revnum(stmt, 5);
3557 if (recorded_revision)
3558 *recorded_revision = svn_sqlite__column_revnum(stmt, 6);
3562 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3563 _("The node '%s' is not an external."),
3564 svn_dirent_local_style(local_abspath,
3568 return svn_error_trace(
3569 svn_error_compose_create(err, svn_sqlite__reset(stmt)));
3573 svn_wc__db_committable_externals_below(apr_array_header_t **externals,
3575 const char *local_abspath,
3576 svn_boolean_t immediates_only,
3577 apr_pool_t *result_pool,
3578 apr_pool_t *scratch_pool)
3580 svn_wc__db_wcroot_t *wcroot;
3581 svn_sqlite__stmt_t *stmt;
3582 const char *local_relpath;
3583 svn_boolean_t have_row;
3584 svn_wc__committable_external_info_t *info;
3585 svn_node_kind_t db_kind;
3586 apr_array_header_t *result = NULL;
3588 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3590 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3591 local_abspath, scratch_pool, scratch_pool));
3592 VERIFY_USABLE_WCROOT(wcroot);
3594 SVN_ERR(svn_sqlite__get_statement(
3597 ? STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW
3598 : STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW));
3600 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3602 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3605 result = apr_array_make(result_pool, 0,
3606 sizeof(svn_wc__committable_external_info_t *));
3610 info = apr_palloc(result_pool, sizeof(*info));
3612 local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3613 info->local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
3616 db_kind = svn_sqlite__column_token(stmt, 1, kind_map);
3617 SVN_ERR_ASSERT(db_kind == svn_node_file || db_kind == svn_node_dir);
3618 info->kind = db_kind;
3620 info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
3621 info->repos_root_url = svn_sqlite__column_text(stmt, 3, result_pool);
3623 APR_ARRAY_PUSH(result, svn_wc__committable_external_info_t *) = info;
3625 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3628 *externals = result;
3629 return svn_error_trace(svn_sqlite__reset(stmt));
3633 svn_wc__db_externals_defined_below(apr_hash_t **externals,
3635 const char *local_abspath,
3636 apr_pool_t *result_pool,
3637 apr_pool_t *scratch_pool)
3639 svn_wc__db_wcroot_t *wcroot;
3640 svn_sqlite__stmt_t *stmt;
3641 const char *local_relpath;
3642 svn_boolean_t have_row;
3644 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3646 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3647 local_abspath, scratch_pool, scratch_pool));
3648 VERIFY_USABLE_WCROOT(wcroot);
3650 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3651 STMT_SELECT_EXTERNALS_DEFINED));
3653 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3655 *externals = apr_hash_make(result_pool);
3656 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3660 const char *def_local_relpath;
3662 local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3663 def_local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3665 svn_hash_sets(*externals,
3666 svn_dirent_join(wcroot->abspath, local_relpath,
3668 svn_dirent_join(wcroot->abspath, def_local_relpath,
3671 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3674 return svn_error_trace(svn_sqlite__reset(stmt));
3678 svn_wc__db_externals_gather_definitions(apr_hash_t **externals,
3679 apr_hash_t **depths,
3681 const char *local_abspath,
3682 apr_pool_t *result_pool,
3683 apr_pool_t *scratch_pool)
3685 svn_wc__db_wcroot_t *wcroot;
3686 svn_sqlite__stmt_t *stmt;
3687 const char *local_relpath;
3688 svn_boolean_t have_row;
3689 svn_error_t *err = NULL;
3690 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3692 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3694 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3695 local_abspath, scratch_pool, iterpool));
3696 VERIFY_USABLE_WCROOT(wcroot);
3698 *externals = apr_hash_make(result_pool);
3700 *depths = apr_hash_make(result_pool);
3702 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3703 STMT_SELECT_EXTERNAL_PROPERTIES));
3705 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3707 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3711 apr_hash_t *node_props;
3712 const char *external_value;
3714 svn_pool_clear(iterpool);
3715 err = svn_sqlite__column_properties(&node_props, stmt, 0, iterpool,
3721 external_value = svn_prop_get_value(node_props, SVN_PROP_EXTERNALS);
3725 const char *node_abspath;
3726 const char *node_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3728 node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
3731 svn_hash_sets(*externals, node_abspath,
3732 apr_pstrdup(result_pool, external_value));
3737 = svn_sqlite__column_token_null(stmt, 2, depth_map,
3740 svn_hash_sets(*depths, node_abspath,
3741 /* Use static string */
3742 svn_token__to_word(depth_map, depth));
3746 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3749 svn_pool_destroy(iterpool);
3751 return svn_error_trace(svn_error_compose_create(err,
3752 svn_sqlite__reset(stmt)));
3755 /* Copy the ACTUAL data for SRC_RELPATH and tweak it to refer to DST_RELPATH.
3756 The new ACTUAL data won't have any conflicts. */
3757 static svn_error_t *
3758 copy_actual(svn_wc__db_wcroot_t *src_wcroot,
3759 const char *src_relpath,
3760 svn_wc__db_wcroot_t *dst_wcroot,
3761 const char *dst_relpath,
3762 apr_pool_t *scratch_pool)
3764 svn_sqlite__stmt_t *stmt;
3765 svn_boolean_t have_row;
3767 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
3768 STMT_SELECT_ACTUAL_NODE));
3769 SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
3770 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3773 apr_size_t props_size;
3774 const char *changelist;
3775 const char *properties;
3777 /* Skipping conflict data... */
3778 changelist = svn_sqlite__column_text(stmt, 0, scratch_pool);
3779 /* No need to parse the properties when simply copying. */
3780 properties = svn_sqlite__column_blob(stmt, 1, &props_size, scratch_pool);
3782 if (changelist || properties)
3784 SVN_ERR(svn_sqlite__reset(stmt));
3786 SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
3787 STMT_INSERT_ACTUAL_NODE));
3788 SVN_ERR(svn_sqlite__bindf(stmt, "issbs",
3789 dst_wcroot->wc_id, dst_relpath,
3790 svn_relpath_dirname(dst_relpath, scratch_pool),
3791 properties, props_size, changelist));
3792 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3795 SVN_ERR(svn_sqlite__reset(stmt));
3797 return SVN_NO_ERROR;
3800 /* Helper for svn_wc__db_op_copy to handle copying from one db to
3802 static svn_error_t *
3803 cross_db_copy(svn_wc__db_wcroot_t *src_wcroot,
3804 const char *src_relpath,
3805 svn_wc__db_wcroot_t *dst_wcroot,
3806 const char *dst_relpath,
3807 svn_wc__db_status_t dst_status,
3809 int dst_np_op_depth,
3810 svn_node_kind_t kind,
3811 const apr_array_header_t *children,
3812 apr_int64_t copyfrom_id,
3813 const char *copyfrom_relpath,
3814 svn_revnum_t copyfrom_rev,
3815 apr_pool_t *scratch_pool)
3817 insert_working_baton_t iwb;
3818 svn_revnum_t changed_rev;
3819 apr_time_t changed_date;
3820 const char *changed_author;
3821 const svn_checksum_t *checksum;
3825 SVN_ERR_ASSERT(kind == svn_node_file
3826 || kind == svn_node_dir
3829 SVN_ERR(read_info(NULL, NULL, NULL, NULL, NULL,
3830 &changed_rev, &changed_date, &changed_author, &depth,
3831 &checksum, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3832 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3833 src_wcroot, src_relpath, scratch_pool, scratch_pool));
3835 if (dst_status != svn_wc__db_status_not_present
3836 && dst_status != svn_wc__db_status_excluded
3837 && dst_status != svn_wc__db_status_server_excluded)
3839 SVN_ERR(db_read_pristine_props(&props, src_wcroot, src_relpath, FALSE,
3840 scratch_pool, scratch_pool));
3846 iwb.presence = dst_status;
3850 iwb.changed_rev = changed_rev;
3851 iwb.changed_date = changed_date;
3852 iwb.changed_author = changed_author;
3853 iwb.original_repos_id = copyfrom_id;
3854 iwb.original_repos_relpath = copyfrom_relpath;
3855 iwb.original_revnum = copyfrom_rev;
3856 iwb.moved_here = FALSE;
3858 iwb.op_depth = dst_op_depth;
3860 iwb.checksum = checksum;
3861 iwb.children = children;
3864 iwb.not_present_op_depth = dst_np_op_depth;
3866 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath, scratch_pool));
3868 SVN_ERR(copy_actual(src_wcroot, src_relpath,
3869 dst_wcroot, dst_relpath, scratch_pool));
3871 return SVN_NO_ERROR;
3874 /* Helper for scan_deletion_txn. Extracts the moved-to information, if
3875 any, from STMT. Sets *SCAN to FALSE if moved-to was available. */
3876 static svn_error_t *
3877 get_moved_to(const char **moved_to_relpath_p,
3878 const char **moved_to_op_root_relpath_p,
3879 svn_boolean_t *scan,
3880 svn_sqlite__stmt_t *stmt,
3881 const char *current_relpath,
3882 svn_wc__db_wcroot_t *wcroot,
3883 const char *local_relpath,
3884 apr_pool_t *result_pool,
3885 apr_pool_t *scratch_pool)
3887 const char *moved_to_relpath = svn_sqlite__column_text(stmt, 3, NULL);
3889 if (moved_to_relpath)
3891 const char *moved_to_op_root_relpath = moved_to_relpath;
3893 if (strcmp(current_relpath, local_relpath))
3895 /* LOCAL_RELPATH is a child inside the move op-root. */
3896 const char *moved_child_relpath;
3898 /* The CURRENT_RELPATH is the op_root of the delete-half of
3899 * the move. LOCAL_RELPATH is a child that was moved along.
3900 * Compute the child's new location within the move target. */
3901 moved_child_relpath = svn_relpath_skip_ancestor(current_relpath,
3903 SVN_ERR_ASSERT(moved_child_relpath &&
3904 strlen(moved_child_relpath) > 0);
3905 moved_to_relpath = svn_relpath_join(moved_to_op_root_relpath,
3906 moved_child_relpath,
3910 if (moved_to_op_root_relpath && moved_to_op_root_relpath_p)
3911 *moved_to_op_root_relpath_p
3912 = apr_pstrdup(result_pool, moved_to_op_root_relpath);
3914 if (moved_to_relpath && moved_to_relpath_p)
3916 = apr_pstrdup(result_pool, moved_to_relpath);
3921 return SVN_NO_ERROR;
3925 /* The body of svn_wc__db_scan_deletion().
3927 static svn_error_t *
3928 scan_deletion_txn(const char **base_del_relpath,
3929 const char **moved_to_relpath,
3930 const char **work_del_relpath,
3931 const char **moved_to_op_root_relpath,
3932 svn_wc__db_wcroot_t *wcroot,
3933 const char *local_relpath,
3934 apr_pool_t *result_pool,
3935 apr_pool_t *scratch_pool)
3937 const char *current_relpath = local_relpath;
3938 svn_sqlite__stmt_t *stmt;
3939 svn_wc__db_status_t work_presence;
3940 svn_boolean_t have_row, scan, have_base;
3943 /* Initialize all the OUT parameters. */
3944 if (base_del_relpath != NULL)
3945 *base_del_relpath = NULL;
3946 if (moved_to_relpath != NULL)
3947 *moved_to_relpath = NULL;
3948 if (work_del_relpath != NULL)
3949 *work_del_relpath = NULL;
3950 if (moved_to_op_root_relpath != NULL)
3951 *moved_to_op_root_relpath = NULL;
3953 /* If looking for moved-to info then we need to scan every path
3954 until we find it. If not looking for moved-to we only need to
3955 check op-roots and parents of op-roots. */
3956 scan = (moved_to_op_root_relpath || moved_to_relpath);
3958 SVN_ERR(svn_sqlite__get_statement(
3960 scan ? STMT_SELECT_DELETION_INFO_SCAN
3961 : STMT_SELECT_DELETION_INFO));
3963 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
3964 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3966 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
3967 _("The node '%s' was not found."),
3968 path_for_error_message(wcroot, local_relpath,
3971 work_presence = svn_sqlite__column_token(stmt, 1, presence_map);
3972 have_base = !svn_sqlite__column_is_null(stmt, 0);
3973 if (work_presence != svn_wc__db_status_not_present
3974 && work_presence != svn_wc__db_status_base_deleted)
3975 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
3976 svn_sqlite__reset(stmt),
3977 _("Expected node '%s' to be deleted."),
3978 path_for_error_message(wcroot, local_relpath,
3981 op_depth = svn_sqlite__column_int(stmt, 2);
3983 /* Special case: LOCAL_RELPATH not-present within a WORKING tree, we
3984 treat this as an op-root. At commit time we need to explicitly
3985 delete such nodes otherwise they will be present in the
3987 if (work_presence == svn_wc__db_status_not_present
3988 && work_del_relpath && !*work_del_relpath)
3990 *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
3992 if (!scan && !base_del_relpath)
3994 /* We have all we need, exit early */
3995 SVN_ERR(svn_sqlite__reset(stmt));
3996 return SVN_NO_ERROR;
4004 const char *parent_relpath;
4005 int current_depth = relpath_depth(current_relpath);
4007 /* Step CURRENT_RELPATH to op-root */
4013 err = get_moved_to(moved_to_relpath, moved_to_op_root_relpath,
4014 &scan, stmt, current_relpath,
4015 wcroot, local_relpath,
4016 result_pool, scratch_pool);
4018 && !base_del_relpath
4019 && !work_del_relpath))
4021 /* We have all we need (or an error occurred) */
4022 SVN_ERR(svn_sqlite__reset(stmt));
4023 return svn_error_trace(err);
4027 if (current_depth <= op_depth)
4030 current_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4033 if (scan || current_depth == op_depth)
4035 SVN_ERR(svn_sqlite__reset(stmt));
4036 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
4038 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4039 SVN_ERR_ASSERT(have_row);
4040 have_base = !svn_sqlite__column_is_null(stmt, 0);
4043 SVN_ERR(svn_sqlite__reset(stmt));
4045 /* Now CURRENT_RELPATH is an op-root, have a look at the parent. */
4047 SVN_ERR_ASSERT(current_relpath[0] != '\0'); /* Catch invalid data */
4048 parent_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4049 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4050 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4053 /* No row means no WORKING node which mean we just fell off
4054 the WORKING tree, so CURRENT_RELPATH is the op-root
4055 closest to the wc root. */
4056 if (have_base && base_del_relpath)
4057 *base_del_relpath = apr_pstrdup(result_pool, current_relpath);
4061 /* Still in the WORKING tree so the first time we get here
4062 CURRENT_RELPATH is a delete op-root in the WORKING tree. */
4063 if (work_del_relpath && !*work_del_relpath)
4065 *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
4067 if (!scan && !base_del_relpath)
4068 break; /* We have all we need */
4071 current_relpath = parent_relpath;
4072 op_depth = svn_sqlite__column_int(stmt, 2);
4073 have_base = !svn_sqlite__column_is_null(stmt, 0);
4076 SVN_ERR(svn_sqlite__reset(stmt));
4078 return SVN_NO_ERROR;
4082 svn_wc__db_scan_deletion(const char **base_del_abspath,
4083 const char **moved_to_abspath,
4084 const char **work_del_abspath,
4085 const char **moved_to_op_root_abspath,
4087 const char *local_abspath,
4088 apr_pool_t *result_pool,
4089 apr_pool_t *scratch_pool)
4091 svn_wc__db_wcroot_t *wcroot;
4092 const char *local_relpath;
4093 const char *base_del_relpath, *moved_to_relpath, *work_del_relpath;
4094 const char *moved_to_op_root_relpath;
4096 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4098 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4099 local_abspath, scratch_pool, scratch_pool));
4100 VERIFY_USABLE_WCROOT(wcroot);
4102 SVN_WC__DB_WITH_TXN(
4103 scan_deletion_txn(&base_del_relpath, &moved_to_relpath,
4104 &work_del_relpath, &moved_to_op_root_relpath,
4105 wcroot, local_relpath, result_pool, scratch_pool),
4108 if (base_del_abspath)
4110 *base_del_abspath = (base_del_relpath
4111 ? svn_dirent_join(wcroot->abspath,
4112 base_del_relpath, result_pool)
4115 if (moved_to_abspath)
4117 *moved_to_abspath = (moved_to_relpath
4118 ? svn_dirent_join(wcroot->abspath,
4119 moved_to_relpath, result_pool)
4122 if (work_del_abspath)
4124 *work_del_abspath = (work_del_relpath
4125 ? svn_dirent_join(wcroot->abspath,
4126 work_del_relpath, result_pool)
4129 if (moved_to_op_root_abspath)
4131 *moved_to_op_root_abspath = (moved_to_op_root_relpath
4132 ? svn_dirent_join(wcroot->abspath,
4133 moved_to_op_root_relpath,
4138 return SVN_NO_ERROR;
4142 /* Set *COPYFROM_ID, *COPYFROM_RELPATH, *COPYFROM_REV to the values
4143 appropriate for the copy. Also return *STATUS, *KIND and *HAVE_WORK, *OP_ROOT
4144 since they are available. This is a helper for
4145 svn_wc__db_op_copy. */
4146 static svn_error_t *
4147 get_info_for_copy(apr_int64_t *copyfrom_id,
4148 const char **copyfrom_relpath,
4149 svn_revnum_t *copyfrom_rev,
4150 svn_wc__db_status_t *status,
4151 svn_node_kind_t *kind,
4152 svn_boolean_t *op_root,
4153 svn_wc__db_wcroot_t *src_wcroot,
4154 const char *local_relpath,
4155 svn_wc__db_wcroot_t *dst_wcroot,
4156 apr_pool_t *result_pool,
4157 apr_pool_t *scratch_pool)
4159 const char *repos_relpath;
4160 svn_revnum_t revision;
4161 svn_wc__db_status_t node_status;
4162 apr_int64_t repos_id;
4163 svn_boolean_t is_op_root;
4165 SVN_ERR(read_info(&node_status, kind, &revision, &repos_relpath, &repos_id,
4166 NULL, NULL, NULL, NULL, NULL, NULL, copyfrom_relpath,
4167 copyfrom_id, copyfrom_rev, NULL, NULL, NULL, NULL,
4168 NULL, &is_op_root, NULL, NULL,
4169 NULL /* have_base */,
4170 NULL /* have_more_work */,
4171 NULL /* have_work */,
4172 src_wcroot, local_relpath, result_pool, scratch_pool));
4175 *op_root = is_op_root;
4177 if (node_status == svn_wc__db_status_excluded)
4179 /* The parent cannot be excluded, so look at the parent and then
4180 adjust the relpath */
4181 const char *parent_relpath, *base_name;
4183 svn_dirent_split(&parent_relpath, &base_name, local_relpath,
4185 SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev,
4187 src_wcroot, parent_relpath, dst_wcroot,
4188 scratch_pool, scratch_pool));
4189 if (*copyfrom_relpath)
4190 *copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, base_name,
4193 else if (node_status == svn_wc__db_status_added)
4195 SVN_ERR(scan_addition(&node_status, NULL, NULL, NULL, NULL, NULL, NULL,
4196 NULL, NULL, NULL, src_wcroot, local_relpath,
4197 scratch_pool, scratch_pool));
4199 else if (node_status == svn_wc__db_status_deleted && is_op_root)
4201 const char *base_del_relpath, *work_del_relpath;
4203 SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
4205 NULL, src_wcroot, local_relpath,
4206 scratch_pool, scratch_pool));
4207 if (work_del_relpath)
4209 const char *op_root_relpath;
4210 const char *parent_del_relpath = svn_relpath_dirname(work_del_relpath,
4213 /* Similar to, but not the same as, the _scan_addition and
4214 _join above. Can we use get_copyfrom here? */
4215 SVN_ERR(scan_addition(NULL, &op_root_relpath,
4216 NULL, NULL, /* repos_* */
4217 copyfrom_relpath, copyfrom_id, copyfrom_rev,
4219 src_wcroot, parent_del_relpath,
4220 scratch_pool, scratch_pool));
4222 = svn_relpath_join(*copyfrom_relpath,
4223 svn_relpath_skip_ancestor(op_root_relpath,
4227 else if (base_del_relpath)
4229 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, copyfrom_rev,
4231 copyfrom_id, NULL, NULL,
4232 NULL, NULL, NULL, NULL,
4233 NULL, NULL, NULL, NULL,
4234 src_wcroot, local_relpath,
4239 SVN_ERR_MALFUNCTION();
4241 else if (node_status == svn_wc__db_status_deleted)
4243 /* Keep original_* from read_info() to allow seeing the difference
4244 between base-deleted and not present */
4248 *copyfrom_relpath = repos_relpath;
4249 *copyfrom_rev = revision;
4250 *copyfrom_id = repos_id;
4254 *status = node_status;
4256 if (src_wcroot != dst_wcroot && *copyfrom_relpath)
4258 const char *repos_root_url;
4259 const char *repos_uuid;
4261 /* Pass the right repos-id for the destination db. We can't just use
4262 the id of the source database, as this value can change after
4263 relocation (and perhaps also when we start storing multiple
4264 working copies in a single db)! */
4266 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
4267 src_wcroot->sdb, *copyfrom_id,
4270 SVN_ERR(create_repos_id(copyfrom_id, repos_root_url, repos_uuid,
4271 dst_wcroot->sdb, scratch_pool));
4274 return SVN_NO_ERROR;
4278 /* Set *OP_DEPTH to the highest op depth of WCROOT:LOCAL_RELPATH. */
4279 static svn_error_t *
4280 op_depth_of(int *op_depth,
4281 svn_wc__db_wcroot_t *wcroot,
4282 const char *local_relpath)
4284 svn_sqlite__stmt_t *stmt;
4285 svn_boolean_t have_row;
4287 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4288 STMT_SELECT_NODE_INFO));
4289 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4290 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4291 SVN_ERR_ASSERT(have_row);
4292 *op_depth = svn_sqlite__column_int(stmt, 0);
4293 SVN_ERR(svn_sqlite__reset(stmt));
4295 return SVN_NO_ERROR;
4299 /* Determine at which OP_DEPTH a copy of COPYFROM_REPOS_ID, COPYFROM_RELPATH at
4300 revision COPYFROM_REVISION should be inserted as LOCAL_RELPATH. Do this
4301 by checking if this would be a direct child of a copy of its parent
4302 directory. If it is then set *OP_DEPTH to the op_depth of its parent.
4304 If the node is not a direct copy at the same revision of the parent
4305 *NP_OP_DEPTH will be set to the op_depth of the parent when a not-present
4306 node should be inserted at this op_depth. This will be the case when the
4307 parent already defined an incomplete child with the same name. Otherwise
4308 *NP_OP_DEPTH will be set to -1.
4310 If the parent node is not the parent of the to be copied node, then
4311 *OP_DEPTH will be set to the proper op_depth for a new operation root.
4313 Set *PARENT_OP_DEPTH to the op_depth of the parent.
4316 static svn_error_t *
4317 op_depth_for_copy(int *op_depth,
4319 int *parent_op_depth,
4320 apr_int64_t copyfrom_repos_id,
4321 const char *copyfrom_relpath,
4322 svn_revnum_t copyfrom_revision,
4323 svn_wc__db_wcroot_t *wcroot,
4324 const char *local_relpath,
4325 apr_pool_t *scratch_pool)
4327 const char *parent_relpath, *name;
4328 svn_sqlite__stmt_t *stmt;
4329 svn_boolean_t have_row;
4330 int incomplete_op_depth = -1;
4331 int min_op_depth = 1; /* Never touch BASE */
4333 *op_depth = relpath_depth(local_relpath);
4336 svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool);
4337 *parent_op_depth = relpath_depth(parent_relpath);
4339 if (!copyfrom_relpath)
4340 return SVN_NO_ERROR;
4342 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4343 STMT_SELECT_WORKING_NODE));
4344 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4345 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4348 svn_wc__db_status_t status = svn_sqlite__column_token(stmt, 1,
4351 min_op_depth = svn_sqlite__column_int(stmt, 0);
4352 if (status == svn_wc__db_status_incomplete)
4353 incomplete_op_depth = min_op_depth;
4355 SVN_ERR(svn_sqlite__reset(stmt));
4357 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4358 STMT_SELECT_WORKING_NODE));
4359 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4360 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4363 svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 1,
4366 *parent_op_depth = svn_sqlite__column_int(stmt, 0);
4367 if (*parent_op_depth < min_op_depth)
4369 /* We want to create a copy; not overwrite the lower layers */
4370 SVN_ERR(svn_sqlite__reset(stmt));
4371 return SVN_NO_ERROR;
4374 /* You can only add children below a node that exists.
4375 In WORKING that must be status added, which is represented
4376 as presence normal */
4377 SVN_ERR_ASSERT(presence == svn_wc__db_status_normal);
4379 if ((incomplete_op_depth < 0)
4380 || (incomplete_op_depth == *parent_op_depth))
4382 apr_int64_t parent_copyfrom_repos_id
4383 = svn_sqlite__column_int64(stmt, 10);
4384 const char *parent_copyfrom_relpath
4385 = svn_sqlite__column_text(stmt, 11, NULL);
4386 svn_revnum_t parent_copyfrom_revision
4387 = svn_sqlite__column_revnum(stmt, 12);
4389 if (parent_copyfrom_repos_id == copyfrom_repos_id)
4391 if (copyfrom_revision == parent_copyfrom_revision
4392 && !strcmp(copyfrom_relpath,
4393 svn_relpath_join(parent_copyfrom_relpath, name,
4395 *op_depth = *parent_op_depth;
4396 else if (incomplete_op_depth > 0)
4397 *np_op_depth = incomplete_op_depth;
4401 SVN_ERR(svn_sqlite__reset(stmt));
4403 return SVN_NO_ERROR;
4407 /* Like svn_wc__db_op_copy(), but with WCROOT+LOCAL_RELPATH
4408 * instead of DB+LOCAL_ABSPATH. A non-zero MOVE_OP_DEPTH implies that the
4409 * copy operation is part of a move, and indicates the op-depth of the
4410 * move destination op-root. */
4411 static svn_error_t *
4412 db_op_copy(svn_wc__db_wcroot_t *src_wcroot,
4413 const char *src_relpath,
4414 svn_wc__db_wcroot_t *dst_wcroot,
4415 const char *dst_relpath,
4416 const svn_skel_t *work_items,
4418 apr_pool_t *scratch_pool)
4420 const char *copyfrom_relpath;
4421 svn_revnum_t copyfrom_rev;
4422 svn_wc__db_status_t status;
4423 svn_wc__db_status_t dst_presence;
4424 svn_boolean_t op_root;
4425 apr_int64_t copyfrom_id;
4427 int dst_np_op_depth;
4428 int dst_parent_op_depth;
4429 svn_node_kind_t kind;
4430 const apr_array_header_t *children;
4432 SVN_ERR(get_info_for_copy(©from_id, ©from_relpath, ©from_rev,
4433 &status, &kind, &op_root,
4434 src_wcroot, src_relpath, dst_wcroot,
4435 scratch_pool, scratch_pool));
4437 SVN_ERR(op_depth_for_copy(&dst_op_depth, &dst_np_op_depth,
4438 &dst_parent_op_depth,
4439 copyfrom_id, copyfrom_relpath, copyfrom_rev,
4440 dst_wcroot, dst_relpath, scratch_pool));
4442 SVN_ERR_ASSERT(kind == svn_node_file || kind == svn_node_dir);
4444 /* ### New status, not finished, see notes/wc-ng/copying */
4447 case svn_wc__db_status_normal:
4448 case svn_wc__db_status_added:
4449 case svn_wc__db_status_moved_here:
4450 case svn_wc__db_status_copied:
4451 dst_presence = svn_wc__db_status_normal;
4453 case svn_wc__db_status_deleted:
4456 /* If the lower layer is already shadowcopied we can skip adding
4457 a not present node. */
4459 svn_wc__db_status_t dst_status;
4461 err = read_info(&dst_status, NULL, NULL, NULL, NULL, NULL, NULL,
4462 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4463 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4464 dst_wcroot, dst_relpath, scratch_pool, scratch_pool);
4468 if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
4469 svn_error_clear(err);
4471 return svn_error_trace(err);
4473 else if (dst_status == svn_wc__db_status_deleted)
4475 /* Node is already deleted; skip the NODES work, but do
4476 install wq items if requested */
4477 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4479 return SVN_NO_ERROR;
4484 /* This node is either a not-present node (which should be copied), or
4485 a base-delete of some lower layer (which shouldn't).
4486 Subversion <= 1.7 always added a not-present node here, which is
4487 safe (as it postpones the hard work until commit time and then we
4488 ask the repository), but it breaks some move scenarios.
4491 if (! copyfrom_relpath)
4493 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4495 return SVN_NO_ERROR;
4498 /* Fall through. Install not present node */
4500 case svn_wc__db_status_not_present:
4501 case svn_wc__db_status_excluded:
4502 /* These presence values should not create a new op depth */
4503 if (dst_np_op_depth > 0)
4505 dst_op_depth = dst_np_op_depth;
4506 dst_np_op_depth = -1;
4508 if (status == svn_wc__db_status_excluded)
4509 dst_presence = svn_wc__db_status_excluded;
4511 dst_presence = svn_wc__db_status_not_present;
4513 case svn_wc__db_status_server_excluded:
4514 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4515 _("Cannot copy '%s' excluded by server"),
4516 path_for_error_message(src_wcroot,
4520 /* Perhaps we should allow incomplete to incomplete? We can't
4521 avoid incomplete working nodes as one step in copying a
4522 directory is to add incomplete children. */
4523 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4524 _("Cannot handle status of '%s'"),
4525 path_for_error_message(src_wcroot,
4530 if (kind == svn_node_dir)
4534 SVN_ERR(op_depth_of(&src_op_depth, src_wcroot, src_relpath));
4535 SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
4536 src_op_depth, scratch_pool, scratch_pool));
4541 if (src_wcroot == dst_wcroot)
4543 svn_sqlite__stmt_t *stmt;
4544 const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
4547 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
4548 STMT_INSERT_WORKING_NODE_COPY_FROM));
4550 SVN_ERR(svn_sqlite__bindf(stmt, "issdst",
4551 src_wcroot->wc_id, src_relpath,
4555 presence_map, dst_presence));
4557 if (move_op_depth > 0)
4559 if (relpath_depth(dst_relpath) == move_op_depth)
4561 /* We're moving the root of the move operation.
4563 * When an added node or the op-root of a copy is moved,
4564 * there is no 'moved-from' corresponding to the moved-here
4565 * node. So the net effect is the same as copy+delete.
4566 * Perform a normal copy operation in these cases. */
4567 if (!(status == svn_wc__db_status_added ||
4568 (status == svn_wc__db_status_copied && op_root)))
4569 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4573 svn_sqlite__stmt_t *info_stmt;
4574 svn_boolean_t have_row;
4576 /* We're moving a child along with the root of the move.
4578 * Set moved-here depending on dst_parent, propagating the
4579 * above decision to moved-along children at the same op_depth.
4580 * We can't use scan_addition() to detect moved-here because
4581 * the delete-half of the move might not yet exist. */
4582 SVN_ERR(svn_sqlite__get_statement(&info_stmt, dst_wcroot->sdb,
4583 STMT_SELECT_NODE_INFO));
4584 SVN_ERR(svn_sqlite__bindf(info_stmt, "is", dst_wcroot->wc_id,
4585 dst_parent_relpath));
4586 SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4587 SVN_ERR_ASSERT(have_row);
4588 if (svn_sqlite__column_boolean(info_stmt, 15) &&
4589 dst_op_depth == dst_parent_op_depth)
4591 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4592 SVN_ERR(svn_sqlite__reset(info_stmt));
4596 SVN_ERR(svn_sqlite__reset(info_stmt));
4598 /* If the child has been moved into the tree we're moving,
4599 * keep its moved-here bit set. */
4600 SVN_ERR(svn_sqlite__get_statement(&info_stmt,
4602 STMT_SELECT_NODE_INFO));
4603 SVN_ERR(svn_sqlite__bindf(info_stmt, "is",
4604 dst_wcroot->wc_id, src_relpath));
4605 SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4606 SVN_ERR_ASSERT(have_row);
4607 if (svn_sqlite__column_boolean(info_stmt, 15))
4608 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4609 SVN_ERR(svn_sqlite__reset(info_stmt));
4614 SVN_ERR(svn_sqlite__step_done(stmt));
4616 /* ### Copying changelist is OK for a move but what about a copy? */
4617 SVN_ERR(copy_actual(src_wcroot, src_relpath,
4618 dst_wcroot, dst_relpath, scratch_pool));
4620 if (dst_np_op_depth > 0)
4622 /* We introduce a not-present node at the parent's op_depth to
4623 properly start a new op-depth at our own op_depth. This marks
4624 us as an op_root for commit and allows reverting just this
4627 SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
4629 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
4630 src_wcroot->wc_id, dst_relpath,
4631 dst_np_op_depth, dst_parent_relpath,
4632 copyfrom_id, copyfrom_relpath,
4635 svn_wc__db_status_not_present,
4639 SVN_ERR(svn_sqlite__step_done(stmt));
4641 /* Insert incomplete children, if relevant.
4642 The children are part of the same op and so have the same op_depth.
4643 (The only time we'd want a different depth is during a recursive
4644 simple add, but we never insert children here during a simple add.) */
4645 if (kind == svn_node_dir
4646 && dst_presence == svn_wc__db_status_normal)
4647 SVN_ERR(insert_incomplete_children(
4660 SVN_ERR(cross_db_copy(src_wcroot, src_relpath, dst_wcroot,
4661 dst_relpath, dst_presence, dst_op_depth,
4662 dst_np_op_depth, kind,
4663 children, copyfrom_id, copyfrom_relpath,
4664 copyfrom_rev, scratch_pool));
4667 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items, scratch_pool));
4669 return SVN_NO_ERROR;
4672 /* Baton for passing args to op_copy_txn(). */
4673 struct op_copy_baton
4675 svn_wc__db_wcroot_t *src_wcroot;
4676 const char *src_relpath;
4678 svn_wc__db_wcroot_t *dst_wcroot;
4679 const char *dst_relpath;
4681 const svn_skel_t *work_items;
4683 svn_boolean_t is_move;
4684 const char *dst_op_root_relpath;
4687 /* Helper for svn_wc__db_op_copy().
4689 * Implements svn_sqlite__transaction_callback_t. */
4690 static svn_error_t *
4691 op_copy_txn(void * baton,
4692 svn_sqlite__db_t *sdb,
4693 apr_pool_t *scratch_pool)
4695 struct op_copy_baton *ocb = baton;
4698 if (sdb != ocb->dst_wcroot->sdb)
4700 /* Source and destination databases differ; so also start a lock
4701 in the destination database, by calling ourself in a lock. */
4703 return svn_error_trace(
4704 svn_sqlite__with_lock(ocb->dst_wcroot->sdb,
4705 op_copy_txn, ocb, scratch_pool));
4708 /* From this point we can assume a lock in the src and dst databases */
4711 move_op_depth = relpath_depth(ocb->dst_op_root_relpath);
4715 SVN_ERR(db_op_copy(ocb->src_wcroot, ocb->src_relpath,
4716 ocb->dst_wcroot, ocb->dst_relpath,
4717 ocb->work_items, move_op_depth, scratch_pool));
4719 return SVN_NO_ERROR;
4723 svn_wc__db_op_copy(svn_wc__db_t *db,
4724 const char *src_abspath,
4725 const char *dst_abspath,
4726 const char *dst_op_root_abspath,
4727 svn_boolean_t is_move,
4728 const svn_skel_t *work_items,
4729 apr_pool_t *scratch_pool)
4731 struct op_copy_baton ocb = {0};
4733 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
4734 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
4735 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_op_root_abspath));
4737 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
4738 &ocb.src_relpath, db,
4740 scratch_pool, scratch_pool));
4741 VERIFY_USABLE_WCROOT(ocb.src_wcroot);
4743 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
4746 scratch_pool, scratch_pool));
4747 VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
4749 ocb.work_items = work_items;
4750 ocb.is_move = is_move;
4751 ocb.dst_op_root_relpath = svn_dirent_skip_ancestor(ocb.dst_wcroot->abspath,
4752 dst_op_root_abspath);
4754 /* Call with the sdb in src_wcroot. It might call itself again to
4755 also obtain a lock in dst_wcroot */
4756 SVN_ERR(svn_sqlite__with_lock(ocb.src_wcroot->sdb, op_copy_txn, &ocb,
4759 return SVN_NO_ERROR;
4762 /* The txn body of svn_wc__db_op_handle_move_back */
4763 static svn_error_t *
4764 handle_move_back(svn_boolean_t *moved_back,
4765 svn_wc__db_wcroot_t *wcroot,
4766 const char *local_relpath,
4767 const char *moved_from_relpath,
4768 const svn_skel_t *work_items,
4769 apr_pool_t *scratch_pool)
4771 svn_sqlite__stmt_t *stmt;
4772 svn_wc__db_status_t status;
4773 svn_boolean_t op_root;
4774 svn_boolean_t have_more_work;
4775 int from_op_depth = 0;
4776 svn_boolean_t have_row;
4777 svn_boolean_t different = FALSE;
4779 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
4781 SVN_ERR(svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL, NULL,
4782 NULL, NULL, NULL, NULL, NULL, NULL,
4783 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4784 &op_root, NULL, NULL, NULL,
4785 &have_more_work, NULL,
4786 wcroot, local_relpath,
4787 scratch_pool, scratch_pool));
4789 if (status != svn_wc__db_status_added || !op_root)
4790 return SVN_NO_ERROR;
4792 /* We have two cases here: BASE-move-back and WORKING-move-back */
4794 SVN_ERR(op_depth_of(&from_op_depth, wcroot,
4795 svn_relpath_dirname(local_relpath, scratch_pool)));
4799 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4800 STMT_SELECT_MOVED_BACK));
4802 SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id,
4805 relpath_depth(local_relpath)));
4807 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4809 SVN_ERR_ASSERT(have_row); /* We checked that the node is an op-root */
4812 svn_boolean_t moved_here = svn_sqlite__column_boolean(stmt, 9);
4813 const char *moved_to = svn_sqlite__column_text(stmt, 10, NULL);
4817 || strcmp(moved_to, moved_from_relpath))
4826 svn_wc__db_status_t upper_status;
4827 svn_wc__db_status_t lower_status;
4829 upper_status = svn_sqlite__column_token(stmt, 1, presence_map);
4831 if (svn_sqlite__column_is_null(stmt, 5))
4833 /* No lower layer replaced. */
4834 if (upper_status != svn_wc__db_status_not_present)
4842 lower_status = svn_sqlite__column_token(stmt, 5, presence_map);
4844 if (upper_status != lower_status)
4850 if (upper_status == svn_wc__db_status_not_present
4851 || upper_status == svn_wc__db_status_excluded)
4853 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4854 continue; /* Nothing to check */
4856 else if (upper_status != svn_wc__db_status_normal)
4858 /* Not a normal move. Mixed revision move? */
4864 const char *upper_repos_relpath;
4865 const char *lower_repos_relpath;
4867 upper_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL);
4868 lower_repos_relpath = svn_sqlite__column_text(stmt, 7, NULL);
4870 if (! upper_repos_relpath
4871 || strcmp(upper_repos_relpath, lower_repos_relpath))
4879 svn_revnum_t upper_rev;
4880 svn_revnum_t lower_rev;
4882 upper_rev = svn_sqlite__column_revnum(stmt, 4);
4883 lower_rev = svn_sqlite__column_revnum(stmt, 8);
4885 if (upper_rev != lower_rev)
4893 apr_int64_t upper_repos_id;
4894 apr_int64_t lower_repos_id;
4896 upper_repos_id = svn_sqlite__column_int64(stmt, 2);
4897 lower_repos_id = svn_sqlite__column_int64(stmt, 6);
4899 if (upper_repos_id != lower_repos_id)
4906 /* Check moved_here? */
4908 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4910 SVN_ERR(svn_sqlite__reset(stmt));
4914 /* Ok, we can now safely remove this complete move, because we
4915 determined that it 100% matches the layer below it. */
4917 /* ### We could copy the recorded timestamps from the higher to the
4918 lower layer in an attempt to improve status performance, but
4919 generally these values should be the same anyway as it was
4921 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4922 STMT_DELETE_MOVED_BACK));
4924 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
4926 relpath_depth(local_relpath)));
4928 SVN_ERR(svn_sqlite__step_done(stmt));
4934 return SVN_NO_ERROR;
4938 svn_wc__db_op_handle_move_back(svn_boolean_t *moved_back,
4940 const char *local_abspath,
4941 const char *moved_from_abspath,
4942 const svn_skel_t *work_items,
4943 apr_pool_t *scratch_pool)
4945 svn_wc__db_wcroot_t *wcroot;
4946 const char *local_relpath;
4947 const char *moved_from_relpath;
4948 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4950 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4952 scratch_pool, scratch_pool));
4953 VERIFY_USABLE_WCROOT(wcroot);
4956 *moved_back = FALSE;
4958 moved_from_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
4959 moved_from_abspath);
4961 if (! local_relpath[0]
4962 || !moved_from_relpath)
4964 /* WC-Roots can't be moved */
4965 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
4966 return SVN_NO_ERROR;
4969 SVN_WC__DB_WITH_TXN(handle_move_back(moved_back, wcroot, local_relpath,
4970 moved_from_relpath, work_items,
4974 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
4977 return SVN_NO_ERROR;
4981 /* The recursive implementation of svn_wc__db_op_copy_shadowed_layer.
4983 * A non-zero MOVE_OP_DEPTH implies that the copy operation is part of
4984 * a move, and indicates the op-depth of the move destination op-root. */
4985 static svn_error_t *
4986 db_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot,
4987 const char *src_relpath,
4989 svn_wc__db_wcroot_t *dst_wcroot,
4990 const char *dst_relpath,
4993 apr_int64_t repos_id,
4994 const char *repos_relpath,
4995 svn_revnum_t revision,
4997 apr_pool_t *scratch_pool)
4999 const apr_array_header_t *children;
5000 apr_pool_t *iterpool;
5001 svn_wc__db_status_t status;
5002 svn_node_kind_t kind;
5003 svn_revnum_t node_revision;
5004 const char *node_repos_relpath;
5005 apr_int64_t node_repos_id;
5006 svn_sqlite__stmt_t *stmt;
5007 svn_wc__db_status_t dst_presence;
5012 err = svn_wc__db_depth_get_info(&status, &kind, &node_revision,
5013 &node_repos_relpath, &node_repos_id,
5014 NULL, NULL, NULL, NULL, NULL, NULL,
5016 src_wcroot, src_relpath, src_op_depth,
5017 scratch_pool, scratch_pool);
5021 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5022 return svn_error_trace(err);
5024 svn_error_clear(err);
5025 return SVN_NO_ERROR; /* There is no shadowed node at src_op_depth */
5029 if (src_op_depth == 0)
5031 /* If the node is switched or has a different revision then its parent
5032 we shouldn't copy it. (We can't as we would have to insert it at
5033 an unshadowed depth) */
5034 if (status == svn_wc__db_status_not_present
5035 || status == svn_wc__db_status_excluded
5036 || status == svn_wc__db_status_server_excluded
5037 || node_revision != revision
5038 || node_repos_id != repos_id
5039 || strcmp(node_repos_relpath, repos_relpath))
5041 /* Add a not-present node in the destination wcroot */
5042 struct insert_working_baton_t iwb;
5043 const char *repos_root_url;
5044 const char *repos_uuid;
5046 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
5047 src_wcroot->sdb, node_repos_id,
5050 SVN_ERR(create_repos_id(&node_repos_id, repos_root_url, repos_uuid,
5051 dst_wcroot->sdb, scratch_pool));
5055 iwb.op_depth = dst_op_depth;
5056 if (status != svn_wc__db_status_excluded)
5057 iwb.presence = svn_wc__db_status_not_present;
5059 iwb.presence = svn_wc__db_status_excluded;
5063 iwb.original_repos_id = node_repos_id;
5064 iwb.original_revnum = node_revision;
5065 iwb.original_repos_relpath = node_repos_relpath;
5067 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5070 return SVN_NO_ERROR;
5074 iterpool = svn_pool_create(scratch_pool);
5078 case svn_wc__db_status_normal:
5079 case svn_wc__db_status_added:
5080 case svn_wc__db_status_moved_here:
5081 case svn_wc__db_status_copied:
5082 dst_presence = svn_wc__db_status_normal;
5084 case svn_wc__db_status_deleted:
5085 case svn_wc__db_status_not_present:
5086 dst_presence = svn_wc__db_status_not_present;
5088 case svn_wc__db_status_excluded:
5089 dst_presence = svn_wc__db_status_excluded;
5091 case svn_wc__db_status_server_excluded:
5092 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5093 _("Cannot copy '%s' excluded by server"),
5094 path_for_error_message(src_wcroot,
5098 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5099 _("Cannot handle status of '%s'"),
5100 path_for_error_message(src_wcroot,
5105 if (dst_presence == svn_wc__db_status_normal
5106 && src_wcroot == dst_wcroot) /* ### Remove limitation */
5108 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
5109 STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH));
5111 SVN_ERR(svn_sqlite__bindf(stmt, "issdstd",
5112 src_wcroot->wc_id, src_relpath,
5115 svn_relpath_dirname(dst_relpath, iterpool),
5116 presence_map, dst_presence,
5120 if (dst_op_depth == move_op_depth)
5121 SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
5123 SVN_ERR(svn_sqlite__step_done(stmt));
5126 /* And mark it deleted to allow proper shadowing */
5127 struct insert_working_baton_t iwb;
5131 iwb.op_depth = del_op_depth;
5132 iwb.presence = svn_wc__db_status_base_deleted;
5136 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5142 struct insert_working_baton_t iwb;
5143 if (dst_presence == svn_wc__db_status_normal) /* Fallback for multi-db */
5144 dst_presence = svn_wc__db_status_not_present;
5146 /* And mark it deleted to allow proper shadowing */
5150 iwb.op_depth = dst_op_depth;
5151 iwb.presence = dst_presence;
5154 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5158 if (dst_presence == svn_wc__db_status_not_present)
5160 /* Don't create descendants of a not present node! */
5162 /* This code is currently still triggered by copying deleted nodes
5163 between separate working copies. See ### comment above. */
5165 svn_pool_destroy(iterpool);
5166 return SVN_NO_ERROR;
5169 SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
5170 src_op_depth, scratch_pool, iterpool));
5172 for (i = 0; i < children->nelts; i++)
5174 const char *name = APR_ARRAY_IDX(children, i, const char *);
5175 const char *child_src_relpath;
5176 const char *child_dst_relpath;
5177 const char *child_repos_relpath = NULL;
5179 svn_pool_clear(iterpool);
5180 child_src_relpath = svn_relpath_join(src_relpath, name, iterpool);
5181 child_dst_relpath = svn_relpath_join(dst_relpath, name, iterpool);
5184 child_repos_relpath = svn_relpath_join(repos_relpath, name, iterpool);
5186 SVN_ERR(db_op_copy_shadowed_layer(
5187 src_wcroot, child_src_relpath, src_op_depth,
5188 dst_wcroot, child_dst_relpath, dst_op_depth,
5190 repos_id, child_repos_relpath, revision,
5191 move_op_depth, scratch_pool));
5194 svn_pool_destroy(iterpool);
5196 return SVN_NO_ERROR;
5199 /* Helper for svn_wc__db_op_copy_shadowed_layer().
5201 * Implements svn_sqlite__transaction_callback_t. */
5202 static svn_error_t *
5203 op_copy_shadowed_layer_txn(void *baton,
5204 svn_sqlite__db_t *sdb,
5205 apr_pool_t *scratch_pool)
5207 struct op_copy_baton *ocb = baton;
5208 const char *src_parent_relpath;
5209 const char *dst_parent_relpath;
5213 const char *repos_relpath = NULL;
5214 apr_int64_t repos_id = INVALID_REPOS_ID;
5215 svn_revnum_t revision = SVN_INVALID_REVNUM;
5217 if (sdb != ocb->dst_wcroot->sdb)
5219 /* Source and destination databases differ; so also start a lock
5220 in the destination database, by calling ourself in a lock. */
5222 return svn_error_trace(
5223 svn_sqlite__with_lock(ocb->dst_wcroot->sdb,
5224 op_copy_shadowed_layer_txn,
5225 ocb, scratch_pool));
5228 /* From this point we can assume a lock in the src and dst databases */
5231 /* src_relpath and dst_relpath can't be wcroot as we need their parents */
5232 SVN_ERR_ASSERT(*ocb->src_relpath && *ocb->dst_relpath);
5234 src_parent_relpath = svn_relpath_dirname(ocb->src_relpath, scratch_pool);
5235 dst_parent_relpath = svn_relpath_dirname(ocb->dst_relpath, scratch_pool);
5237 /* src_parent must be status normal or added; get its op-depth */
5238 SVN_ERR(op_depth_of(&src_op_depth, ocb->src_wcroot, src_parent_relpath));
5240 /* dst_parent must be status added; get its op-depth */
5241 SVN_ERR(op_depth_of(&dst_op_depth, ocb->dst_wcroot, dst_parent_relpath));
5243 del_op_depth = relpath_depth(ocb->dst_relpath);
5245 /* Get some information from the parent */
5246 SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, &revision, &repos_relpath,
5247 &repos_id, NULL, NULL, NULL, NULL, NULL,
5250 src_parent_relpath, src_op_depth,
5251 scratch_pool, scratch_pool));
5253 if (repos_relpath == NULL)
5255 /* The node is a local addition and has no shadowed information */
5256 return SVN_NO_ERROR;
5259 /* And calculate the child repos relpath */
5260 repos_relpath = svn_relpath_join(repos_relpath,
5261 svn_relpath_basename(ocb->src_relpath,
5265 SVN_ERR(db_op_copy_shadowed_layer(
5266 ocb->src_wcroot, ocb->src_relpath, src_op_depth,
5267 ocb->dst_wcroot, ocb->dst_relpath, dst_op_depth,
5269 repos_id, repos_relpath, revision,
5270 (ocb->is_move ? dst_op_depth : 0),
5273 return SVN_NO_ERROR;
5277 svn_wc__db_op_copy_shadowed_layer(svn_wc__db_t *db,
5278 const char *src_abspath,
5279 const char *dst_abspath,
5280 svn_boolean_t is_move,
5281 apr_pool_t *scratch_pool)
5283 struct op_copy_baton ocb = {0};
5285 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
5286 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
5288 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
5289 &ocb.src_relpath, db,
5291 scratch_pool, scratch_pool));
5292 VERIFY_USABLE_WCROOT(ocb.src_wcroot);
5294 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
5297 scratch_pool, scratch_pool));
5298 VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
5300 ocb.is_move = is_move;
5301 ocb.dst_op_root_relpath = NULL; /* not used by op_copy_shadowed_layer_txn */
5303 ocb.work_items = NULL;
5305 /* Call with the sdb in src_wcroot. It might call itself again to
5306 also obtain a lock in dst_wcroot */
5307 SVN_ERR(svn_sqlite__with_lock(ocb.src_wcroot->sdb,
5308 op_copy_shadowed_layer_txn,
5309 &ocb, scratch_pool));
5311 return SVN_NO_ERROR;
5315 /* If there are any server-excluded base nodes then the copy must fail
5316 as it's not possible to commit such a copy.
5317 Return an error if there are any server-excluded nodes. */
5318 static svn_error_t *
5319 catch_copy_of_server_excluded(svn_wc__db_wcroot_t *wcroot,
5320 const char *local_relpath,
5321 apr_pool_t *scratch_pool)
5323 svn_sqlite__stmt_t *stmt;
5324 svn_boolean_t have_row;
5325 const char *server_excluded_relpath;
5327 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5328 STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
5329 SVN_ERR(svn_sqlite__bindf(stmt, "is",
5332 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5334 server_excluded_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
5335 SVN_ERR(svn_sqlite__reset(stmt));
5337 return svn_error_createf(SVN_ERR_AUTHZ_UNREADABLE, NULL,
5338 _("Cannot copy '%s' excluded by server"),
5339 path_for_error_message(wcroot,
5340 server_excluded_relpath,
5343 return SVN_NO_ERROR;
5348 svn_wc__db_op_copy_dir(svn_wc__db_t *db,
5349 const char *local_abspath,
5350 const apr_hash_t *props,
5351 svn_revnum_t changed_rev,
5352 apr_time_t changed_date,
5353 const char *changed_author,
5354 const char *original_repos_relpath,
5355 const char *original_root_url,
5356 const char *original_uuid,
5357 svn_revnum_t original_revision,
5358 const apr_array_header_t *children,
5360 svn_boolean_t is_move,
5361 const svn_skel_t *conflict,
5362 const svn_skel_t *work_items,
5363 apr_pool_t *scratch_pool)
5365 svn_wc__db_wcroot_t *wcroot;
5366 const char *local_relpath;
5367 insert_working_baton_t iwb;
5368 int parent_op_depth;
5370 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5371 SVN_ERR_ASSERT(props != NULL);
5372 /* ### any assertions for CHANGED_* ? */
5373 /* ### any assertions for ORIGINAL_* ? */
5375 SVN_ERR_ASSERT(children != NULL);
5378 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5379 local_abspath, scratch_pool, scratch_pool));
5380 VERIFY_USABLE_WCROOT(wcroot);
5384 iwb.presence = svn_wc__db_status_normal;
5385 iwb.kind = svn_node_dir;
5387 if (original_root_url != NULL)
5389 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5390 original_root_url, original_uuid,
5391 wcroot->sdb, scratch_pool));
5392 iwb.original_repos_relpath = original_repos_relpath;
5393 iwb.original_revnum = original_revision;
5396 iwb.changed_rev = changed_rev;
5397 iwb.changed_date = changed_date;
5398 iwb.changed_author = changed_author;
5401 /* ### Should we do this inside the transaction? */
5402 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5403 &parent_op_depth, iwb.original_repos_id,
5404 original_repos_relpath, original_revision,
5405 wcroot, local_relpath, scratch_pool));
5407 iwb.children = children;
5409 iwb.moved_here = is_move && (parent_op_depth == 0 ||
5410 iwb.op_depth == parent_op_depth);
5412 iwb.work_items = work_items;
5413 iwb.conflict = conflict;
5415 SVN_WC__DB_WITH_TXN(
5416 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5418 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
5420 return SVN_NO_ERROR;
5425 svn_wc__db_op_copy_file(svn_wc__db_t *db,
5426 const char *local_abspath,
5427 const apr_hash_t *props,
5428 svn_revnum_t changed_rev,
5429 apr_time_t changed_date,
5430 const char *changed_author,
5431 const char *original_repos_relpath,
5432 const char *original_root_url,
5433 const char *original_uuid,
5434 svn_revnum_t original_revision,
5435 const svn_checksum_t *checksum,
5436 svn_boolean_t update_actual_props,
5437 const apr_hash_t *new_actual_props,
5438 svn_boolean_t is_move,
5439 const svn_skel_t *conflict,
5440 const svn_skel_t *work_items,
5441 apr_pool_t *scratch_pool)
5443 svn_wc__db_wcroot_t *wcroot;
5444 const char *local_relpath;
5445 insert_working_baton_t iwb;
5446 int parent_op_depth;
5448 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5449 SVN_ERR_ASSERT(props != NULL);
5450 /* ### any assertions for CHANGED_* ? */
5451 SVN_ERR_ASSERT((! original_repos_relpath && ! original_root_url
5452 && ! original_uuid && ! checksum
5453 && original_revision == SVN_INVALID_REVNUM)
5454 || (original_repos_relpath && original_root_url
5455 && original_uuid && checksum
5456 && original_revision != SVN_INVALID_REVNUM));
5458 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5459 local_abspath, scratch_pool, scratch_pool));
5460 VERIFY_USABLE_WCROOT(wcroot);
5464 iwb.presence = svn_wc__db_status_normal;
5465 iwb.kind = svn_node_file;
5467 if (original_root_url != NULL)
5469 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5470 original_root_url, original_uuid,
5471 wcroot->sdb, scratch_pool));
5472 iwb.original_repos_relpath = original_repos_relpath;
5473 iwb.original_revnum = original_revision;
5476 iwb.changed_rev = changed_rev;
5477 iwb.changed_date = changed_date;
5478 iwb.changed_author = changed_author;
5481 /* ### Should we do this inside the transaction? */
5482 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5483 &parent_op_depth, iwb.original_repos_id,
5484 original_repos_relpath, original_revision,
5485 wcroot, local_relpath, scratch_pool));
5487 iwb.checksum = checksum;
5488 iwb.moved_here = is_move && (parent_op_depth == 0 ||
5489 iwb.op_depth == parent_op_depth);
5491 if (update_actual_props)
5493 iwb.update_actual_props = update_actual_props;
5494 iwb.new_actual_props = new_actual_props;
5497 iwb.work_items = work_items;
5498 iwb.conflict = conflict;
5500 SVN_WC__DB_WITH_TXN(
5501 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5503 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5505 return SVN_NO_ERROR;
5510 svn_wc__db_op_copy_symlink(svn_wc__db_t *db,
5511 const char *local_abspath,
5512 const apr_hash_t *props,
5513 svn_revnum_t changed_rev,
5514 apr_time_t changed_date,
5515 const char *changed_author,
5516 const char *original_repos_relpath,
5517 const char *original_root_url,
5518 const char *original_uuid,
5519 svn_revnum_t original_revision,
5521 svn_boolean_t is_move,
5522 const svn_skel_t *conflict,
5523 const svn_skel_t *work_items,
5524 apr_pool_t *scratch_pool)
5526 svn_wc__db_wcroot_t *wcroot;
5527 const char *local_relpath;
5528 insert_working_baton_t iwb;
5529 int parent_op_depth;
5531 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5532 SVN_ERR_ASSERT(props != NULL);
5533 /* ### any assertions for CHANGED_* ? */
5534 /* ### any assertions for ORIGINAL_* ? */
5535 SVN_ERR_ASSERT(target != NULL);
5537 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5538 local_abspath, scratch_pool, scratch_pool));
5539 VERIFY_USABLE_WCROOT(wcroot);
5543 iwb.presence = svn_wc__db_status_normal;
5544 iwb.kind = svn_node_symlink;
5547 if (original_root_url != NULL)
5549 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5550 original_root_url, original_uuid,
5551 wcroot->sdb, scratch_pool));
5552 iwb.original_repos_relpath = original_repos_relpath;
5553 iwb.original_revnum = original_revision;
5556 iwb.changed_rev = changed_rev;
5557 iwb.changed_date = changed_date;
5558 iwb.changed_author = changed_author;
5561 /* ### Should we do this inside the transaction? */
5562 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5563 &parent_op_depth, iwb.original_repos_id,
5564 original_repos_relpath, original_revision,
5565 wcroot, local_relpath, scratch_pool));
5567 iwb.target = target;
5568 iwb.moved_here = is_move && (parent_op_depth == 0 ||
5569 iwb.op_depth == parent_op_depth);
5571 iwb.work_items = work_items;
5572 iwb.conflict = conflict;
5574 SVN_WC__DB_WITH_TXN(
5575 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5577 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5579 return SVN_NO_ERROR;
5584 svn_wc__db_op_add_directory(svn_wc__db_t *db,
5585 const char *local_abspath,
5586 const apr_hash_t *props,
5587 const svn_skel_t *work_items,
5588 apr_pool_t *scratch_pool)
5590 svn_wc__db_wcroot_t *wcroot;
5591 const char *local_relpath;
5592 const char *dir_abspath;
5594 insert_working_baton_t iwb;
5596 /* Resolve wcroot via parent directory to avoid obstruction handling */
5597 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5598 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5600 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5601 dir_abspath, scratch_pool, scratch_pool));
5602 VERIFY_USABLE_WCROOT(wcroot);
5606 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5607 iwb.presence = svn_wc__db_status_normal;
5608 iwb.kind = svn_node_dir;
5609 iwb.op_depth = relpath_depth(local_relpath);
5610 if (props && apr_hash_count((apr_hash_t *)props))
5612 iwb.update_actual_props = TRUE;
5613 iwb.new_actual_props = props;
5616 iwb.work_items = work_items;
5618 SVN_WC__DB_WITH_TXN(
5619 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5621 /* Use depth infinity to make sure we have no invalid cached information
5622 * about children of this dir. */
5623 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5626 return SVN_NO_ERROR;
5631 svn_wc__db_op_add_file(svn_wc__db_t *db,
5632 const char *local_abspath,
5633 const apr_hash_t *props,
5634 const svn_skel_t *work_items,
5635 apr_pool_t *scratch_pool)
5637 svn_wc__db_wcroot_t *wcroot;
5638 const char *local_relpath;
5639 insert_working_baton_t iwb;
5640 const char *dir_abspath;
5643 /* Resolve wcroot via parent directory to avoid obstruction handling */
5644 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5645 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5647 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5648 dir_abspath, scratch_pool, scratch_pool));
5649 VERIFY_USABLE_WCROOT(wcroot);
5653 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5654 iwb.presence = svn_wc__db_status_normal;
5655 iwb.kind = svn_node_file;
5656 iwb.op_depth = relpath_depth(local_relpath);
5657 if (props && apr_hash_count((apr_hash_t *)props))
5659 iwb.update_actual_props = TRUE;
5660 iwb.new_actual_props = props;
5663 iwb.work_items = work_items;
5665 SVN_WC__DB_WITH_TXN(
5666 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5668 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5670 return SVN_NO_ERROR;
5675 svn_wc__db_op_add_symlink(svn_wc__db_t *db,
5676 const char *local_abspath,
5678 const apr_hash_t *props,
5679 const svn_skel_t *work_items,
5680 apr_pool_t *scratch_pool)
5682 svn_wc__db_wcroot_t *wcroot;
5683 const char *local_relpath;
5684 insert_working_baton_t iwb;
5685 const char *dir_abspath;
5688 /* Resolve wcroot via parent directory to avoid obstruction handling */
5689 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5690 SVN_ERR_ASSERT(target != NULL);
5692 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5694 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5695 dir_abspath, scratch_pool, scratch_pool));
5697 VERIFY_USABLE_WCROOT(wcroot);
5701 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5702 iwb.presence = svn_wc__db_status_normal;
5703 iwb.kind = svn_node_symlink;
5704 iwb.op_depth = relpath_depth(local_relpath);
5705 if (props && apr_hash_count((apr_hash_t *)props))
5707 iwb.update_actual_props = TRUE;
5708 iwb.new_actual_props = props;
5711 iwb.target = target;
5713 iwb.work_items = work_items;
5715 SVN_WC__DB_WITH_TXN(
5716 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5718 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5720 return SVN_NO_ERROR;
5723 /* Record RECORDED_SIZE and RECORDED_TIME into top layer in NODES */
5724 static svn_error_t *
5725 db_record_fileinfo(svn_wc__db_wcroot_t *wcroot,
5726 const char *local_relpath,
5727 apr_int64_t recorded_size,
5728 apr_int64_t recorded_time,
5729 apr_pool_t *scratch_pool)
5731 svn_sqlite__stmt_t *stmt;
5734 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5735 STMT_UPDATE_NODE_FILEINFO));
5736 SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
5737 recorded_size, recorded_time));
5738 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
5740 SVN_ERR_ASSERT(affected_rows == 1);
5742 return SVN_NO_ERROR;
5747 svn_wc__db_global_record_fileinfo(svn_wc__db_t *db,
5748 const char *local_abspath,
5749 svn_filesize_t recorded_size,
5750 apr_time_t recorded_time,
5751 apr_pool_t *scratch_pool)
5753 svn_wc__db_wcroot_t *wcroot;
5754 const char *local_relpath;
5756 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5758 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5759 local_abspath, scratch_pool, scratch_pool));
5760 VERIFY_USABLE_WCROOT(wcroot);
5762 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
5763 recorded_size, recorded_time, scratch_pool));
5765 /* We *totally* monkeyed the entries. Toss 'em. */
5766 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5768 return SVN_NO_ERROR;
5772 /* Set the ACTUAL_NODE properties column for (WC_ID, LOCAL_RELPATH) to
5775 * Note: PROPS=NULL means the actual props are the same as the pristine
5776 * props; to indicate no properties when the pristine has some props,
5777 * PROPS must be an empty hash. */
5778 static svn_error_t *
5779 set_actual_props(apr_int64_t wc_id,
5780 const char *local_relpath,
5782 svn_sqlite__db_t *db,
5783 apr_pool_t *scratch_pool)
5785 svn_sqlite__stmt_t *stmt;
5788 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_UPDATE_ACTUAL_PROPS));
5789 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
5790 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
5791 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
5793 if (affected_rows == 1 || !props)
5794 return SVN_NO_ERROR; /* We are done */
5796 /* We have to insert a row in ACTUAL */
5798 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_ACTUAL_PROPS));
5799 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
5800 if (*local_relpath != '\0')
5801 SVN_ERR(svn_sqlite__bind_text(stmt, 3,
5802 svn_relpath_dirname(local_relpath,
5804 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
5805 return svn_error_trace(svn_sqlite__step_done(stmt));
5809 /* The body of svn_wc__db_op_set_props().
5811 Set the 'properties' column in the 'ACTUAL_NODE' table to BATON->props.
5812 Create an entry in the ACTUAL table for the node if it does not yet
5814 To specify no properties, BATON->props must be an empty hash, not NULL.
5815 BATON is of type 'struct set_props_baton_t'.
5817 static svn_error_t *
5818 set_props_txn(svn_wc__db_wcroot_t *wcroot,
5819 const char *local_relpath,
5821 svn_boolean_t clear_recorded_info,
5822 const svn_skel_t *conflict,
5823 const svn_skel_t *work_items,
5824 apr_pool_t *scratch_pool)
5826 apr_hash_t *pristine_props;
5828 /* Check if the props are modified. If no changes, then wipe out the
5829 ACTUAL props. PRISTINE_PROPS==NULL means that any
5830 ACTUAL props are okay as provided, so go ahead and set them. */
5831 SVN_ERR(db_read_pristine_props(&pristine_props, wcroot, local_relpath, FALSE,
5832 scratch_pool, scratch_pool));
5833 if (props && pristine_props)
5835 apr_array_header_t *prop_diffs;
5837 SVN_ERR(svn_prop_diffs(&prop_diffs, props, pristine_props,
5839 if (prop_diffs->nelts == 0)
5843 SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath,
5844 props, wcroot->sdb, scratch_pool));
5846 if (clear_recorded_info)
5848 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
5849 SVN_INVALID_FILESIZE, 0,
5854 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5856 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
5857 conflict, scratch_pool));
5859 return SVN_NO_ERROR;
5864 svn_wc__db_op_set_props(svn_wc__db_t *db,
5865 const char *local_abspath,
5867 svn_boolean_t clear_recorded_info,
5868 const svn_skel_t *conflict,
5869 const svn_skel_t *work_items,
5870 apr_pool_t *scratch_pool)
5872 svn_wc__db_wcroot_t *wcroot;
5873 const char *local_relpath;
5875 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5877 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
5878 db, local_abspath, scratch_pool, scratch_pool));
5879 VERIFY_USABLE_WCROOT(wcroot);
5881 SVN_WC__DB_WITH_TXN(set_props_txn(wcroot, local_relpath, props,
5882 clear_recorded_info, conflict, work_items,
5885 return SVN_NO_ERROR;
5890 svn_wc__db_op_modified(svn_wc__db_t *db,
5891 const char *local_abspath,
5892 apr_pool_t *scratch_pool)
5894 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5900 static svn_error_t *
5901 populate_targets_tree(svn_wc__db_wcroot_t *wcroot,
5902 const char *local_relpath,
5904 const apr_array_header_t *changelist_filter,
5905 apr_pool_t *scratch_pool)
5907 svn_sqlite__stmt_t *stmt;
5908 int affected_rows = 0;
5909 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
5910 STMT_CREATE_TARGETS_LIST));
5912 if (changelist_filter && changelist_filter->nelts > 0)
5914 /* Iterate over the changelists, adding the nodes which match.
5915 Common case: we only have one changelist, so this only
5922 case svn_depth_empty:
5923 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST;
5926 case svn_depth_files:
5927 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES;
5930 case svn_depth_immediates:
5931 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES;
5934 case svn_depth_infinity:
5935 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY;
5939 /* We don't know how to handle unknown or exclude. */
5940 SVN_ERR_MALFUNCTION();
5944 for (i = 0; i < changelist_filter->nelts; i++)
5947 const char *changelist = APR_ARRAY_IDX(changelist_filter, i,
5950 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5951 STMT_INSERT_TARGET_WITH_CHANGELIST));
5952 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
5953 local_relpath, changelist));
5954 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5956 /* If the root is matched by the changelist, we don't have to match
5957 the children. As that tells us the root is a file */
5958 if (!sub_affected && depth > svn_depth_empty)
5960 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
5961 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
5962 local_relpath, changelist));
5963 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5966 affected_rows += sub_affected;
5969 else /* No changelist filtering */
5976 case svn_depth_empty:
5977 stmt_idx = STMT_INSERT_TARGET;
5980 case svn_depth_files:
5981 stmt_idx = STMT_INSERT_TARGET_DEPTH_FILES;
5984 case svn_depth_immediates:
5985 stmt_idx = STMT_INSERT_TARGET_DEPTH_IMMEDIATES;
5988 case svn_depth_infinity:
5989 stmt_idx = STMT_INSERT_TARGET_DEPTH_INFINITY;
5993 /* We don't know how to handle unknown or exclude. */
5994 SVN_ERR_MALFUNCTION();
5998 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5999 STMT_INSERT_TARGET));
6000 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6001 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6002 affected_rows += sub_affected;
6004 if (depth > svn_depth_empty)
6006 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
6007 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6008 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6009 affected_rows += sub_affected;
6013 /* Does the target exist? */
6014 if (affected_rows == 0)
6016 svn_boolean_t exists;
6017 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
6020 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6021 _("The node '%s' was not found."),
6022 path_for_error_message(wcroot,
6027 return SVN_NO_ERROR;
6032 static svn_error_t *
6033 dump_targets(svn_wc__db_wcroot_t *wcroot,
6034 apr_pool_t *scratch_pool)
6036 svn_sqlite__stmt_t *stmt;
6037 svn_boolean_t have_row;
6039 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6040 STMT_SELECT_TARGETS));
6041 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6044 const char *target = svn_sqlite__column_text(stmt, 0, NULL);
6045 SVN_DBG(("Target: '%s'\n", target));
6046 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6049 SVN_ERR(svn_sqlite__reset(stmt));
6051 return SVN_NO_ERROR;
6056 struct set_changelist_baton_t
6058 const char *new_changelist;
6059 const apr_array_header_t *changelist_filter;
6064 /* The main part of svn_wc__db_op_set_changelist().
6066 * Implements svn_wc__db_txn_callback_t. */
6067 static svn_error_t *
6068 set_changelist_txn(void *baton,
6069 svn_wc__db_wcroot_t *wcroot,
6070 const char *local_relpath,
6071 apr_pool_t *scratch_pool)
6073 struct set_changelist_baton_t *scb = baton;
6074 svn_sqlite__stmt_t *stmt;
6076 SVN_ERR(populate_targets_tree(wcroot, local_relpath, scb->depth,
6077 scb->changelist_filter, scratch_pool));
6079 /* Ensure we have actual nodes for our targets. */
6080 if (scb->new_changelist)
6082 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6083 STMT_INSERT_ACTUAL_EMPTIES));
6084 SVN_ERR(svn_sqlite__step_done(stmt));
6087 /* Now create our notification table. */
6088 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6089 STMT_CREATE_CHANGELIST_LIST));
6090 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6091 STMT_CREATE_CHANGELIST_TRIGGER));
6093 /* Update our changelists. */
6094 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6095 STMT_UPDATE_ACTUAL_CHANGELISTS));
6096 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6097 scb->new_changelist));
6098 SVN_ERR(svn_sqlite__step_done(stmt));
6100 if (scb->new_changelist)
6102 /* We have to notify that we skipped directories, so do that now. */
6103 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6104 STMT_MARK_SKIPPED_CHANGELIST_DIRS));
6105 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6106 scb->new_changelist));
6107 SVN_ERR(svn_sqlite__step_done(stmt));
6110 /* We may have left empty ACTUAL nodes, so remove them. This is only a
6111 potential problem if we removed changelists. */
6112 if (!scb->new_changelist)
6114 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6115 STMT_DELETE_ACTUAL_EMPTIES));
6116 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6117 SVN_ERR(svn_sqlite__step_done(stmt));
6120 return SVN_NO_ERROR;
6124 /* Send notifications for svn_wc__db_op_set_changelist().
6126 * Implements work_callback_t. */
6127 static svn_error_t *
6128 do_changelist_notify(void *baton,
6129 svn_wc__db_wcroot_t *wcroot,
6130 svn_cancel_func_t cancel_func,
6132 svn_wc_notify_func2_t notify_func,
6134 apr_pool_t *scratch_pool)
6136 svn_sqlite__stmt_t *stmt;
6137 svn_boolean_t have_row;
6138 apr_pool_t *iterpool;
6140 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6141 STMT_SELECT_CHANGELIST_LIST));
6142 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6144 iterpool = svn_pool_create(scratch_pool);
6147 /* ### wc_id is column 0. use it one day... */
6148 const char *notify_relpath = svn_sqlite__column_text(stmt, 1, NULL);
6149 svn_wc_notify_action_t action = svn_sqlite__column_int(stmt, 2);
6150 svn_wc_notify_t *notify;
6151 const char *notify_abspath;
6153 svn_pool_clear(iterpool);
6157 svn_error_t *err = cancel_func(cancel_baton);
6160 return svn_error_trace(svn_error_compose_create(
6162 svn_sqlite__reset(stmt)));
6165 notify_abspath = svn_dirent_join(wcroot->abspath, notify_relpath,
6167 notify = svn_wc_create_notify(notify_abspath, action, iterpool);
6168 notify->changelist_name = svn_sqlite__column_text(stmt, 3, NULL);
6169 notify_func(notify_baton, notify, iterpool);
6171 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6173 svn_pool_destroy(iterpool);
6175 return svn_error_trace(svn_sqlite__reset(stmt));
6180 svn_wc__db_op_set_changelist(svn_wc__db_t *db,
6181 const char *local_abspath,
6182 const char *new_changelist,
6183 const apr_array_header_t *changelist_filter,
6185 svn_wc_notify_func2_t notify_func,
6187 svn_cancel_func_t cancel_func,
6189 apr_pool_t *scratch_pool)
6191 svn_wc__db_wcroot_t *wcroot;
6192 const char *local_relpath;
6193 struct set_changelist_baton_t scb;
6195 scb.new_changelist = new_changelist;
6196 scb.changelist_filter = changelist_filter;
6199 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6201 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6203 scratch_pool, scratch_pool));
6204 VERIFY_USABLE_WCROOT(wcroot);
6206 /* Flush the entries before we do the work. Even if no work is performed,
6207 the flush isn't a problem. */
6208 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6210 /* Perform the set-changelist operation (transactionally), perform any
6211 notifications necessary, and then clean out our temporary tables. */
6212 return svn_error_trace(with_finalization(wcroot, local_relpath,
6213 set_changelist_txn, &scb,
6214 do_changelist_notify, NULL,
6215 cancel_func, cancel_baton,
6216 notify_func, notify_baton,
6217 STMT_FINALIZE_CHANGELIST,
6221 /* Implementation of svn_wc__db_op_mark_conflict() */
6223 svn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t *wcroot,
6224 const char *local_relpath,
6225 const svn_skel_t *conflict_skel,
6226 apr_pool_t *scratch_pool)
6228 svn_sqlite__stmt_t *stmt;
6229 svn_boolean_t got_row;
6230 svn_boolean_t is_complete;
6232 SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict_skel));
6233 SVN_ERR_ASSERT(is_complete);
6235 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6236 STMT_SELECT_ACTUAL_NODE));
6237 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6238 SVN_ERR(svn_sqlite__step(&got_row, stmt));
6239 SVN_ERR(svn_sqlite__reset(stmt));
6243 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6244 STMT_UPDATE_ACTUAL_CONFLICT));
6245 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6249 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6250 STMT_INSERT_ACTUAL_CONFLICT));
6251 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6252 if (*local_relpath != '\0')
6253 SVN_ERR(svn_sqlite__bind_text(stmt, 4,
6254 svn_relpath_dirname(local_relpath,
6259 svn_stringbuf_t *sb = svn_skel__unparse(conflict_skel, scratch_pool);
6261 SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6264 SVN_ERR(svn_sqlite__update(NULL, stmt));
6266 return SVN_NO_ERROR;
6270 svn_wc__db_op_mark_conflict(svn_wc__db_t *db,
6271 const char *local_abspath,
6272 const svn_skel_t *conflict_skel,
6273 const svn_skel_t *work_items,
6274 apr_pool_t *scratch_pool)
6276 svn_wc__db_wcroot_t *wcroot;
6277 const char *local_relpath;
6279 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6281 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6282 local_abspath, scratch_pool, scratch_pool));
6283 VERIFY_USABLE_WCROOT(wcroot);
6285 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6286 conflict_skel, scratch_pool));
6288 /* ### Should be handled in the same transaction as setting the conflict */
6290 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6292 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6294 return SVN_NO_ERROR;
6298 /* The body of svn_wc__db_op_mark_resolved().
6300 static svn_error_t *
6301 db_op_mark_resolved(svn_wc__db_wcroot_t *wcroot,
6302 const char *local_relpath,
6304 svn_boolean_t resolved_text,
6305 svn_boolean_t resolved_props,
6306 svn_boolean_t resolved_tree,
6307 const svn_skel_t *work_items,
6308 apr_pool_t *scratch_pool)
6310 svn_sqlite__stmt_t *stmt;
6311 svn_boolean_t have_row;
6312 int total_affected_rows = 0;
6313 svn_boolean_t resolved_all;
6314 apr_size_t conflict_len;
6315 const void *conflict_data;
6316 svn_skel_t *conflicts;
6318 /* Check if we have a conflict in ACTUAL */
6319 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6320 STMT_SELECT_ACTUAL_NODE));
6321 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6323 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6327 SVN_ERR(svn_sqlite__reset(stmt));
6329 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6330 STMT_SELECT_NODE_INFO));
6332 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6334 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6335 SVN_ERR(svn_sqlite__reset(stmt));
6338 return SVN_NO_ERROR;
6340 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6341 _("The node '%s' was not found."),
6342 path_for_error_message(wcroot,
6347 conflict_data = svn_sqlite__column_blob(stmt, 2, &conflict_len,
6349 conflicts = svn_skel__parse(conflict_data, conflict_len, scratch_pool);
6350 SVN_ERR(svn_sqlite__reset(stmt));
6352 SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
6353 db, wcroot->abspath,
6355 resolved_props ? "" : NULL,
6357 scratch_pool, scratch_pool));
6359 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6360 STMT_UPDATE_ACTUAL_CONFLICT));
6361 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6365 svn_stringbuf_t *sb = svn_skel__unparse(conflicts, scratch_pool);
6367 SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6370 SVN_ERR(svn_sqlite__update(&total_affected_rows, stmt));
6372 /* Now, remove the actual node if it doesn't have any more useful
6373 information. We only need to do this if we've remove data ourselves. */
6374 if (total_affected_rows > 0)
6376 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6377 STMT_DELETE_ACTUAL_EMPTY));
6378 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6379 SVN_ERR(svn_sqlite__step_done(stmt));
6382 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6384 return SVN_NO_ERROR;
6388 svn_wc__db_op_mark_resolved(svn_wc__db_t *db,
6389 const char *local_abspath,
6390 svn_boolean_t resolved_text,
6391 svn_boolean_t resolved_props,
6392 svn_boolean_t resolved_tree,
6393 const svn_skel_t *work_items,
6394 apr_pool_t *scratch_pool)
6396 svn_wc__db_wcroot_t *wcroot;
6397 const char *local_relpath;
6399 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6401 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6402 local_abspath, scratch_pool, scratch_pool));
6403 VERIFY_USABLE_WCROOT(wcroot);
6405 SVN_WC__DB_WITH_TXN(
6406 db_op_mark_resolved(wcroot, local_relpath, db,
6407 resolved_text, resolved_props, resolved_tree,
6408 work_items, scratch_pool),
6411 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6412 return SVN_NO_ERROR;
6415 /* Clear moved-to information at the delete-half of the move which
6416 * moved LOCAL_RELPATH here. This transforms the move into a simple delete. */
6417 static svn_error_t *
6418 clear_moved_to(const char *local_relpath,
6419 svn_wc__db_wcroot_t *wcroot,
6420 apr_pool_t *scratch_pool)
6422 svn_sqlite__stmt_t *stmt;
6423 svn_boolean_t have_row;
6424 const char *moved_from_relpath;
6426 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6427 STMT_SELECT_MOVED_FROM_RELPATH));
6428 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6429 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6432 SVN_ERR(svn_sqlite__reset(stmt));
6433 return SVN_NO_ERROR;
6436 moved_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
6437 SVN_ERR(svn_sqlite__reset(stmt));
6439 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6440 STMT_CLEAR_MOVED_TO_RELPATH));
6441 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6443 relpath_depth(moved_from_relpath)));
6444 SVN_ERR(svn_sqlite__step_done(stmt));
6446 return SVN_NO_ERROR;
6449 /* One of the two alternative bodies of svn_wc__db_op_revert().
6451 * Implements svn_wc__db_txn_callback_t. */
6452 static svn_error_t *
6453 op_revert_txn(void *baton,
6454 svn_wc__db_wcroot_t *wcroot,
6455 const char *local_relpath,
6456 apr_pool_t *scratch_pool)
6458 svn_wc__db_t *db = baton;
6459 svn_sqlite__stmt_t *stmt;
6460 svn_boolean_t have_row;
6462 svn_boolean_t moved_here;
6464 const char *moved_to;
6466 /* ### Similar structure to op_revert_recursive_txn, should they be
6469 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6470 STMT_SELECT_NODE_INFO));
6471 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6472 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6475 SVN_ERR(svn_sqlite__reset(stmt));
6477 /* There was no NODE row, so attempt to delete an ACTUAL_NODE row. */
6478 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6479 STMT_DELETE_ACTUAL_NODE));
6480 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6481 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6484 /* Can't do non-recursive actual-only revert if actual-only
6485 children exist. Raise an error to cancel the transaction. */
6486 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6487 STMT_ACTUAL_HAS_CHILDREN));
6488 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6489 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6490 SVN_ERR(svn_sqlite__reset(stmt));
6492 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6493 _("Can't revert '%s' without"
6494 " reverting children"),
6495 path_for_error_message(wcroot,
6498 return SVN_NO_ERROR;
6501 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6502 _("The node '%s' was not found."),
6503 path_for_error_message(wcroot,
6508 op_depth = svn_sqlite__column_int(stmt, 0);
6509 moved_here = svn_sqlite__column_boolean(stmt, 15);
6510 moved_to = svn_sqlite__column_text(stmt, 17, scratch_pool);
6511 SVN_ERR(svn_sqlite__reset(stmt));
6515 SVN_ERR(svn_wc__db_resolve_break_moved_away_internal(wcroot,
6522 svn_skel_t *conflict;
6524 SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, wcroot,
6526 scratch_pool, scratch_pool));
6529 svn_wc_operation_t operation;
6530 svn_boolean_t tree_conflicted;
6532 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
6534 db, wcroot->abspath,
6536 scratch_pool, scratch_pool));
6538 && (operation == svn_wc_operation_update
6539 || operation == svn_wc_operation_switch))
6541 svn_wc_conflict_reason_t reason;
6542 svn_wc_conflict_action_t action;
6544 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
6546 db, wcroot->abspath,
6551 if (reason == svn_wc_conflict_reason_deleted)
6552 SVN_ERR(svn_wc__db_resolve_delete_raise_moved_away(
6553 db, svn_dirent_join(wcroot->abspath, local_relpath,
6555 NULL, NULL /* ### How do we notify this? */,
6561 if (op_depth > 0 && op_depth == relpath_depth(local_relpath))
6563 /* Can't do non-recursive revert if children exist */
6564 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6565 STMT_SELECT_GE_OP_DEPTH_CHILDREN));
6566 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6567 local_relpath, op_depth));
6568 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6569 SVN_ERR(svn_sqlite__reset(stmt));
6571 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6572 _("Can't revert '%s' without"
6573 " reverting children"),
6574 path_for_error_message(wcroot,
6578 /* Rewrite the op-depth of all deleted children making the
6579 direct children into roots of deletes. */
6580 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6581 STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE));
6582 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6585 SVN_ERR(svn_sqlite__step_done(stmt));
6587 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6588 STMT_DELETE_WORKING_NODE));
6589 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6590 SVN_ERR(svn_sqlite__step_done(stmt));
6592 /* ### This removes the lock, but what about the access baton? */
6593 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6594 STMT_DELETE_WC_LOCK_ORPHAN));
6595 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6596 SVN_ERR(svn_sqlite__step_done(stmt));
6598 /* If this node was moved-here, clear moved-to at the move source. */
6600 SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool));
6603 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6604 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST));
6605 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6606 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6609 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6610 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST));
6611 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6612 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6615 return SVN_NO_ERROR;
6619 /* One of the two alternative bodies of svn_wc__db_op_revert().
6621 * Implements svn_wc__db_txn_callback_t. */
6622 static svn_error_t *
6623 op_revert_recursive_txn(void *baton,
6624 svn_wc__db_wcroot_t *wcroot,
6625 const char *local_relpath,
6626 apr_pool_t *scratch_pool)
6628 svn_sqlite__stmt_t *stmt;
6629 svn_boolean_t have_row;
6631 int select_op_depth;
6632 svn_boolean_t moved_here;
6634 apr_pool_t *iterpool;
6636 /* ### Similar structure to op_revert_txn, should they be
6639 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6640 STMT_SELECT_NODE_INFO));
6641 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6642 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6645 SVN_ERR(svn_sqlite__reset(stmt));
6647 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6648 STMT_DELETE_ACTUAL_NODE));
6649 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
6651 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6654 return SVN_NO_ERROR; /* actual-only revert */
6656 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6657 _("The node '%s' was not found."),
6658 path_for_error_message(wcroot,
6663 op_depth = svn_sqlite__column_int(stmt, 0);
6664 moved_here = svn_sqlite__column_boolean(stmt, 15);
6665 SVN_ERR(svn_sqlite__reset(stmt));
6667 if (op_depth > 0 && op_depth != relpath_depth(local_relpath))
6668 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6669 _("Can't revert '%s' without"
6670 " reverting parent"),
6671 path_for_error_message(wcroot,
6675 /* Remove moved-here from move destinations outside the tree. */
6676 SVN_ERR(svn_sqlite__get_statement(
6677 &stmt, wcroot->sdb, STMT_SELECT_MOVED_OUTSIDE));
6678 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
6680 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6683 const char *move_src_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6684 int move_op_depth = svn_sqlite__column_int(stmt, 2);
6687 err = svn_wc__db_resolve_break_moved_away_internal(wcroot,
6692 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
6694 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6696 SVN_ERR(svn_sqlite__reset(stmt));
6698 /* Don't delete BASE nodes */
6699 select_op_depth = op_depth ? op_depth : 1;
6701 /* Reverting any non wc-root node */
6702 SVN_ERR(svn_sqlite__get_statement(
6704 STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
6705 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6706 local_relpath, select_op_depth));
6707 SVN_ERR(svn_sqlite__step_done(stmt));
6709 SVN_ERR(svn_sqlite__get_statement(
6711 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
6712 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6713 SVN_ERR(svn_sqlite__step_done(stmt));
6715 SVN_ERR(svn_sqlite__get_statement(
6717 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
6718 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6719 SVN_ERR(svn_sqlite__step_done(stmt));
6721 /* ### This removes the locks, but what about the access batons? */
6722 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6723 STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
6724 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
6726 SVN_ERR(svn_sqlite__step_done(stmt));
6728 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6729 STMT_SELECT_MOVED_HERE_CHILDREN));
6730 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6732 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6734 iterpool = svn_pool_create(scratch_pool);
6737 const char *moved_here_child_relpath;
6740 svn_pool_clear(iterpool);
6742 moved_here_child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
6743 err = clear_moved_to(moved_here_child_relpath, wcroot, iterpool);
6745 return svn_error_trace(svn_error_compose_create(
6747 svn_sqlite__reset(stmt)));
6749 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6751 SVN_ERR(svn_sqlite__reset(stmt));
6752 svn_pool_destroy(iterpool);
6754 /* Clear potential moved-to pointing at the target node itself. */
6755 if (op_depth > 0 && op_depth == relpath_depth(local_relpath)
6757 SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool));
6759 return SVN_NO_ERROR;
6763 svn_wc__db_op_revert(svn_wc__db_t *db,
6764 const char *local_abspath,
6766 apr_pool_t *result_pool,
6767 apr_pool_t *scratch_pool)
6769 svn_wc__db_wcroot_t *wcroot;
6770 const char *local_relpath;
6771 struct with_triggers_baton_t wtb = { STMT_CREATE_REVERT_LIST,
6772 STMT_DROP_REVERT_LIST_TRIGGERS,
6775 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6779 case svn_depth_empty:
6780 wtb.cb_func = op_revert_txn;
6783 case svn_depth_infinity:
6784 wtb.cb_func = op_revert_recursive_txn;
6787 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
6788 _("Unsupported depth for revert of '%s'"),
6789 svn_dirent_local_style(local_abspath,
6793 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6794 db, local_abspath, scratch_pool, scratch_pool));
6795 VERIFY_USABLE_WCROOT(wcroot);
6797 SVN_WC__DB_WITH_TXN(with_triggers(&wtb, wcroot, local_relpath, scratch_pool),
6800 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6802 return SVN_NO_ERROR;
6805 /* The body of svn_wc__db_revert_list_read().
6807 static svn_error_t *
6808 revert_list_read(svn_boolean_t *reverted,
6809 const apr_array_header_t **marker_paths,
6810 svn_boolean_t *copied_here,
6811 svn_node_kind_t *kind,
6812 svn_wc__db_wcroot_t *wcroot,
6813 const char *local_relpath,
6815 apr_pool_t *result_pool,
6816 apr_pool_t *scratch_pool)
6818 svn_sqlite__stmt_t *stmt;
6819 svn_boolean_t have_row;
6822 *marker_paths = NULL;
6823 *copied_here = FALSE;
6824 *kind = svn_node_unknown;
6826 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6827 STMT_SELECT_REVERT_LIST));
6828 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6829 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6832 svn_boolean_t is_actual = svn_sqlite__column_boolean(stmt, 0);
6833 svn_boolean_t another_row = FALSE;
6837 apr_size_t conflict_len;
6838 const void *conflict_data;
6840 conflict_data = svn_sqlite__column_blob(stmt, 5, &conflict_len,
6844 svn_skel_t *conflicts = svn_skel__parse(conflict_data,
6848 SVN_ERR(svn_wc__conflict_read_markers(marker_paths,
6849 db, wcroot->abspath,
6855 if (!svn_sqlite__column_is_null(stmt, 1)) /* notify */
6858 SVN_ERR(svn_sqlite__step(&another_row, stmt));
6861 if (!is_actual || another_row)
6864 if (!svn_sqlite__column_is_null(stmt, 4)) /* repos_id */
6866 int op_depth = svn_sqlite__column_int(stmt, 3);
6867 *copied_here = (op_depth == relpath_depth(local_relpath));
6869 *kind = svn_sqlite__column_token(stmt, 2, kind_map);
6873 SVN_ERR(svn_sqlite__reset(stmt));
6877 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6878 STMT_DELETE_REVERT_LIST));
6879 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6880 SVN_ERR(svn_sqlite__step_done(stmt));
6883 return SVN_NO_ERROR;
6887 svn_wc__db_revert_list_read(svn_boolean_t *reverted,
6888 const apr_array_header_t **marker_files,
6889 svn_boolean_t *copied_here,
6890 svn_node_kind_t *kind,
6892 const char *local_abspath,
6893 apr_pool_t *result_pool,
6894 apr_pool_t *scratch_pool)
6896 svn_wc__db_wcroot_t *wcroot;
6897 const char *local_relpath;
6899 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6900 db, local_abspath, scratch_pool, scratch_pool));
6901 VERIFY_USABLE_WCROOT(wcroot);
6903 SVN_WC__DB_WITH_TXN(
6904 revert_list_read(reverted, marker_files, copied_here, kind,
6905 wcroot, local_relpath, db,
6906 result_pool, scratch_pool),
6908 return SVN_NO_ERROR;
6912 /* The body of svn_wc__db_revert_list_read_copied_children().
6914 static svn_error_t *
6915 revert_list_read_copied_children(svn_wc__db_wcroot_t *wcroot,
6916 const char *local_relpath,
6917 const apr_array_header_t **children_p,
6918 apr_pool_t *result_pool,
6919 apr_pool_t *scratch_pool)
6921 svn_sqlite__stmt_t *stmt;
6922 svn_boolean_t have_row;
6923 apr_array_header_t *children;
6926 apr_array_make(result_pool, 0,
6927 sizeof(svn_wc__db_revert_list_copied_child_info_t *));
6929 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6930 STMT_SELECT_REVERT_LIST_COPIED_CHILDREN));
6931 SVN_ERR(svn_sqlite__bindf(stmt, "sd",
6932 local_relpath, relpath_depth(local_relpath)));
6933 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6936 svn_wc__db_revert_list_copied_child_info_t *child_info;
6937 const char *child_relpath;
6939 child_info = apr_palloc(result_pool, sizeof(*child_info));
6941 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6942 child_info->abspath = svn_dirent_join(wcroot->abspath, child_relpath,
6944 child_info->kind = svn_sqlite__column_token(stmt, 1, kind_map);
6947 svn_wc__db_revert_list_copied_child_info_t *) = child_info;
6949 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6951 SVN_ERR(svn_sqlite__reset(stmt));
6953 *children_p = children;
6955 return SVN_NO_ERROR;
6960 svn_wc__db_revert_list_read_copied_children(const apr_array_header_t **children,
6962 const char *local_abspath,
6963 apr_pool_t *result_pool,
6964 apr_pool_t *scratch_pool)
6966 svn_wc__db_wcroot_t *wcroot;
6967 const char *local_relpath;
6969 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6970 db, local_abspath, scratch_pool, scratch_pool));
6971 VERIFY_USABLE_WCROOT(wcroot);
6973 SVN_WC__DB_WITH_TXN(
6974 revert_list_read_copied_children(wcroot, local_relpath, children,
6975 result_pool, scratch_pool),
6977 return SVN_NO_ERROR;
6982 svn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func,
6985 const char *local_abspath,
6986 apr_pool_t *scratch_pool)
6988 svn_wc__db_wcroot_t *wcroot;
6989 const char *local_relpath;
6990 svn_sqlite__stmt_t *stmt;
6991 svn_boolean_t have_row;
6992 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
6994 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6995 db, local_abspath, scratch_pool, iterpool));
6996 VERIFY_USABLE_WCROOT(wcroot);
6998 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6999 STMT_SELECT_REVERT_LIST_RECURSIVE));
7000 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7001 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7003 return svn_error_trace(svn_sqlite__reset(stmt)); /* optimise for no row */
7006 const char *notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7008 svn_pool_clear(iterpool);
7010 notify_func(notify_baton,
7011 svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
7014 svn_wc_notify_revert,
7018 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7020 SVN_ERR(svn_sqlite__reset(stmt));
7022 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7023 STMT_DELETE_REVERT_LIST_RECURSIVE));
7024 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7025 SVN_ERR(svn_sqlite__step_done(stmt));
7027 svn_pool_destroy(iterpool);
7029 return SVN_NO_ERROR;
7033 svn_wc__db_revert_list_done(svn_wc__db_t *db,
7034 const char *local_abspath,
7035 apr_pool_t *scratch_pool)
7037 svn_wc__db_wcroot_t *wcroot;
7038 const char *local_relpath;
7040 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7041 db, local_abspath, scratch_pool, scratch_pool));
7042 VERIFY_USABLE_WCROOT(wcroot);
7044 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_DROP_REVERT_LIST));
7046 return SVN_NO_ERROR;
7049 /* The body of svn_wc__db_op_remove_node().
7051 static svn_error_t *
7052 remove_node_txn(svn_boolean_t *left_changes,
7053 svn_wc__db_wcroot_t *wcroot,
7054 const char *local_relpath,
7056 svn_boolean_t destroy_wc,
7057 svn_boolean_t destroy_changes,
7058 svn_revnum_t not_present_rev,
7059 svn_wc__db_status_t not_present_status,
7060 svn_node_kind_t not_present_kind,
7061 const svn_skel_t *conflict,
7062 const svn_skel_t *work_items,
7063 svn_cancel_func_t cancel_func,
7065 apr_pool_t *scratch_pool)
7067 svn_sqlite__stmt_t *stmt;
7069 apr_int64_t repos_id;
7070 const char *repos_relpath;
7072 /* Note that unlike many similar functions it is a valid scenario for this
7073 function to be called on a wcroot! */
7075 /* db set when destroying wc */
7076 SVN_ERR_ASSERT(!destroy_wc || db != NULL);
7079 *left_changes = FALSE;
7081 /* Need info for not_present node? */
7082 if (SVN_IS_VALID_REVNUM(not_present_rev))
7083 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
7084 &repos_relpath, &repos_id,
7085 NULL, NULL, NULL, NULL, NULL,
7086 NULL, NULL, NULL, NULL, NULL,
7087 wcroot, local_relpath,
7088 scratch_pool, scratch_pool));
7091 && (!destroy_changes || *local_relpath == '\0'))
7093 svn_boolean_t have_row;
7094 apr_pool_t *iterpool;
7095 svn_error_t *err = NULL;
7097 /* Install WQ items for deleting the unmodified files and all dirs */
7098 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7099 STMT_SELECT_WORKING_PRESENT));
7100 SVN_ERR(svn_sqlite__bindf(stmt, "is",
7101 wcroot->wc_id, local_relpath));
7103 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7105 iterpool = svn_pool_create(scratch_pool);
7109 const char *child_relpath;
7110 const char *child_abspath;
7111 svn_node_kind_t child_kind;
7112 svn_boolean_t have_checksum;
7113 svn_filesize_t recorded_size;
7114 apr_int64_t recorded_time;
7115 const svn_io_dirent2_t *dirent;
7116 svn_boolean_t modified_p = TRUE;
7117 svn_skel_t *work_item = NULL;
7119 svn_pool_clear(iterpool);
7121 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7122 child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
7124 child_abspath = svn_dirent_join(wcroot->abspath, child_relpath,
7127 if (child_kind == svn_node_file)
7129 have_checksum = !svn_sqlite__column_is_null(stmt, 2);
7130 recorded_size = get_recorded_size(stmt, 3);
7131 recorded_time = svn_sqlite__column_int64(stmt, 4);
7135 err = cancel_func(cancel_baton);
7140 err = svn_io_stat_dirent2(&dirent, child_abspath, FALSE, TRUE,
7141 iterpool, iterpool);
7147 || dirent->kind != svn_node_file
7148 || child_kind != svn_node_file)
7150 /* Not interested in keeping changes */
7153 else if (child_kind == svn_node_file
7154 && dirent->kind == svn_node_file
7155 && dirent->filesize == recorded_size
7156 && dirent->mtime == recorded_time)
7158 modified_p = FALSE; /* File matches recorded state */
7160 else if (have_checksum)
7161 err = svn_wc__internal_file_modified_p(&modified_p,
7171 *left_changes = TRUE;
7173 else if (child_kind == svn_node_dir)
7175 err = svn_wc__wq_build_dir_remove(&work_item,
7176 db, wcroot->abspath,
7177 child_abspath, FALSE,
7178 iterpool, iterpool);
7180 else /* svn_node_file || svn_node_symlink */
7182 err = svn_wc__wq_build_file_remove(&work_item,
7183 db, wcroot->abspath,
7185 iterpool, iterpool);
7193 err = add_work_items(wcroot->sdb, work_item, iterpool);
7198 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7200 svn_pool_destroy(iterpool);
7202 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
7205 if (destroy_wc && *local_relpath != '\0')
7207 /* Create work item for destroying the root */
7208 svn_wc__db_status_t status;
7209 svn_node_kind_t kind;
7210 SVN_ERR(read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, NULL,
7211 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7212 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7213 wcroot, local_relpath,
7214 scratch_pool, scratch_pool));
7216 if (status == svn_wc__db_status_normal
7217 || status == svn_wc__db_status_added
7218 || status == svn_wc__db_status_incomplete)
7220 svn_skel_t *work_item = NULL;
7221 const char *local_abspath = svn_dirent_join(wcroot->abspath,
7225 if (kind == svn_node_dir)
7227 SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
7228 db, wcroot->abspath,
7232 scratch_pool, scratch_pool));
7236 svn_boolean_t modified_p = FALSE;
7238 if (!destroy_changes)
7240 SVN_ERR(svn_wc__internal_file_modified_p(&modified_p,
7247 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
7248 db, wcroot->abspath,
7255 *left_changes = TRUE;
7259 SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
7263 /* Remove all nodes below local_relpath */
7264 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7265 STMT_DELETE_NODE_RECURSIVE));
7266 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7267 SVN_ERR(svn_sqlite__step_done(stmt));
7269 /* Delete the root NODE when this is not the working copy root */
7270 if (local_relpath[0] != '\0')
7272 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7273 STMT_DELETE_NODE_ALL_LAYERS));
7274 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7275 SVN_ERR(svn_sqlite__step_done(stmt));
7278 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7279 STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7281 /* Delete all actual nodes at or below local_relpath */
7282 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7284 SVN_ERR(svn_sqlite__step_done(stmt));
7286 /* Should we leave a not-present node? */
7287 if (SVN_IS_VALID_REVNUM(not_present_rev))
7289 insert_base_baton_t ibb;
7292 ibb.repos_id = repos_id;
7294 SVN_ERR_ASSERT(not_present_status == svn_wc__db_status_not_present
7295 || not_present_status == svn_wc__db_status_excluded);
7297 ibb.status = not_present_status;
7298 ibb.kind = not_present_kind;
7300 ibb.repos_relpath = repos_relpath;
7301 ibb.revision = not_present_rev;
7303 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
7306 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
7308 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
7309 conflict, scratch_pool));
7311 return SVN_NO_ERROR;
7315 svn_wc__db_op_remove_node(svn_boolean_t *left_changes,
7317 const char *local_abspath,
7318 svn_boolean_t destroy_wc,
7319 svn_boolean_t destroy_changes,
7320 svn_revnum_t not_present_revision,
7321 svn_wc__db_status_t not_present_status,
7322 svn_node_kind_t not_present_kind,
7323 const svn_skel_t *conflict,
7324 const svn_skel_t *work_items,
7325 svn_cancel_func_t cancel_func,
7327 apr_pool_t *scratch_pool)
7329 svn_wc__db_wcroot_t *wcroot;
7330 const char *local_relpath;
7332 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7334 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7335 local_abspath, scratch_pool, scratch_pool));
7336 VERIFY_USABLE_WCROOT(wcroot);
7338 SVN_WC__DB_WITH_TXN(remove_node_txn(left_changes,
7339 wcroot, local_relpath, db,
7340 destroy_wc, destroy_changes,
7341 not_present_revision, not_present_status,
7342 not_present_kind, conflict, work_items,
7343 cancel_func, cancel_baton, scratch_pool),
7346 /* Flush everything below this node in all ways */
7347 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
7350 return SVN_NO_ERROR;
7354 /* The body of svn_wc__db_op_set_base_depth().
7356 static svn_error_t *
7357 db_op_set_base_depth(svn_wc__db_wcroot_t *wcroot,
7358 const char *local_relpath,
7360 apr_pool_t *scratch_pool)
7362 svn_sqlite__stmt_t *stmt;
7365 /* Flush any entries before we start monkeying the database. */
7366 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7367 STMT_UPDATE_NODE_BASE_DEPTH));
7368 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
7369 svn_token__to_word(depth_map, depth)));
7370 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7372 if (affected_rows == 0)
7373 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7374 "The node '%s' is not a committed directory",
7375 path_for_error_message(wcroot, local_relpath,
7378 return SVN_NO_ERROR;
7383 svn_wc__db_op_set_base_depth(svn_wc__db_t *db,
7384 const char *local_abspath,
7386 apr_pool_t *scratch_pool)
7388 svn_wc__db_wcroot_t *wcroot;
7389 const char *local_relpath;
7391 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7392 SVN_ERR_ASSERT(depth >= svn_depth_empty && depth <= svn_depth_infinity);
7394 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7395 local_abspath, scratch_pool, scratch_pool));
7396 VERIFY_USABLE_WCROOT(wcroot);
7398 /* ### We set depth on working and base to match entry behavior.
7399 Maybe these should be separated later? */
7400 SVN_WC__DB_WITH_TXN(db_op_set_base_depth(wcroot, local_relpath, depth,
7404 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
7406 return SVN_NO_ERROR;
7410 static svn_error_t *
7411 info_below_working(svn_boolean_t *have_base,
7412 svn_boolean_t *have_work,
7413 svn_wc__db_status_t *status,
7414 svn_wc__db_wcroot_t *wcroot,
7415 const char *local_relpath,
7416 int below_op_depth, /* < 0 is ignored */
7417 apr_pool_t *scratch_pool);
7420 /* Convert STATUS, the raw status obtained from the presence map, to
7421 the status appropriate for a working (op_depth > 0) node and return
7422 it in *WORKING_STATUS. */
7423 static svn_error_t *
7424 convert_to_working_status(svn_wc__db_status_t *working_status,
7425 svn_wc__db_status_t status)
7427 svn_wc__db_status_t work_status = status;
7429 SVN_ERR_ASSERT(work_status == svn_wc__db_status_normal
7430 || work_status == svn_wc__db_status_not_present
7431 || work_status == svn_wc__db_status_base_deleted
7432 || work_status == svn_wc__db_status_incomplete
7433 || work_status == svn_wc__db_status_excluded);
7435 if (work_status == svn_wc__db_status_excluded)
7437 *working_status = svn_wc__db_status_excluded;
7439 else if (work_status == svn_wc__db_status_not_present
7440 || work_status == svn_wc__db_status_base_deleted)
7442 /* The caller should scan upwards to detect whether this
7443 deletion has occurred because this node has been moved
7444 away, or it is a regular deletion. Also note that the
7445 deletion could be of the BASE tree, or a child of
7446 something that has been copied/moved here. */
7448 *working_status = svn_wc__db_status_deleted;
7450 else /* normal or incomplete */
7452 /* The caller should scan upwards to detect whether this
7453 addition has occurred because of a simple addition,
7454 a copy, or is the destination of a move. */
7455 *working_status = svn_wc__db_status_added;
7458 return SVN_NO_ERROR;
7462 /* Return the status of the node, if any, below the "working" node (or
7463 below BELOW_OP_DEPTH if >= 0).
7464 Set *HAVE_BASE or *HAVE_WORK to indicate if a base node or lower
7465 working node is present, and *STATUS to the status of the first
7466 layer below the selected node. */
7467 static svn_error_t *
7468 info_below_working(svn_boolean_t *have_base,
7469 svn_boolean_t *have_work,
7470 svn_wc__db_status_t *status,
7471 svn_wc__db_wcroot_t *wcroot,
7472 const char *local_relpath,
7474 apr_pool_t *scratch_pool)
7476 svn_sqlite__stmt_t *stmt;
7477 svn_boolean_t have_row;
7479 *have_base = *have_work = FALSE;
7480 *status = svn_wc__db_status_normal;
7482 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7483 STMT_SELECT_NODE_INFO));
7484 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7485 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7487 if (below_op_depth >= 0)
7490 (svn_sqlite__column_int(stmt, 0) > below_op_depth))
7492 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7497 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7499 *status = svn_sqlite__column_token(stmt, 3, presence_map);
7503 int op_depth = svn_sqlite__column_int(stmt, 0);
7510 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7513 SVN_ERR(svn_sqlite__reset(stmt));
7516 SVN_ERR(convert_to_working_status(status, *status));
7518 return SVN_NO_ERROR;
7521 /* Helper function for op_delete_txn */
7522 static svn_error_t *
7523 delete_update_movedto(svn_wc__db_wcroot_t *wcroot,
7524 const char *child_moved_from_relpath,
7526 const char *new_moved_to_relpath,
7527 apr_pool_t *scratch_pool)
7529 svn_sqlite__stmt_t *stmt;
7532 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7533 STMT_UPDATE_MOVED_TO_RELPATH));
7535 SVN_ERR(svn_sqlite__bindf(stmt, "isds",
7537 child_moved_from_relpath,
7539 new_moved_to_relpath));
7540 SVN_ERR(svn_sqlite__update(&affected, stmt));
7542 /* Not fatal in release mode. The move recording is broken,
7543 but the rest of the working copy can handle this. */
7544 SVN_ERR_ASSERT(affected == 1);
7547 return SVN_NO_ERROR;
7551 struct op_delete_baton_t {
7552 const char *moved_to_relpath; /* NULL if delete is not part of a move */
7553 svn_skel_t *conflict;
7554 svn_skel_t *work_items;
7555 svn_boolean_t delete_dir_externals;
7556 svn_boolean_t notify;
7559 /* This structure is used while rewriting move information for nodes.
7561 * The most simple case of rewriting move information happens when
7562 * a moved-away subtree is moved again: mv A B; mv B C
7563 * The second move requires rewriting moved-to info at or within A.
7565 * Another example is a move of a subtree which had nodes moved into it:
7567 * This requires rewriting such that A/F is marked has having moved to G/F.
7569 * Another case is where a node becomes a nested moved node.
7570 * A nested move happens when a subtree child is moved before or after
7571 * the subtree itself is moved. For example:
7572 * mv A/F A/G; mv A B
7573 * In this case, the move A/F -> A/G is rewritten to B/F -> B/G.
7574 * Note that the following sequence results in the same DB state:
7575 * mv A B; mv B/F B/G
7576 * We do not care about the order the moves were performed in.
7577 * For details, see http://wiki.apache.org/subversion/MultiLayerMoves
7579 struct moved_node_t {
7580 /* The source of the move. */
7581 const char *local_relpath;
7583 /* The move destination. */
7584 const char *moved_to_relpath;
7586 /* The op-depth of the deleted node at the source of the move. */
7589 /* When >= 1 the op_depth at which local_relpath was moved to its
7590 location. Used to find its original location outside the delete */
7591 int moved_from_depth;
7594 /* Helper function to resolve the original location of local_relpath at OP_DEPTH
7595 before it was moved into the tree rooted at ROOT_RELPATH. */
7596 static svn_error_t *
7597 resolve_moved_from(const char **moved_from_relpath,
7598 int *moved_from_op_depth,
7599 svn_wc__db_wcroot_t *wcroot,
7600 const char *root_relpath,
7601 const char *local_relpath,
7603 apr_pool_t *result_pool,
7604 apr_pool_t *scratch_pool)
7606 const char *suffix = "";
7607 svn_sqlite__stmt_t *stmt;
7608 const char *m_from_relpath;
7609 int m_from_op_depth;
7610 int m_move_from_depth;
7611 svn_boolean_t have_row;
7613 while (relpath_depth(local_relpath) > op_depth)
7616 svn_relpath_split(&local_relpath, &name, local_relpath, scratch_pool);
7617 suffix = svn_relpath_join(suffix, name, scratch_pool);
7620 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7621 STMT_SELECT_MOVED_FROM_FOR_DELETE));
7622 SVN_ERR(svn_sqlite__bindf(stmt, "is",
7623 wcroot->wc_id, local_relpath));
7624 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7628 /* assert(have_row); */
7629 *moved_from_relpath = NULL;
7630 *moved_from_op_depth = -1;
7632 SVN_ERR(svn_sqlite__reset(stmt));
7634 return SVN_NO_ERROR;
7637 m_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
7638 m_from_op_depth = svn_sqlite__column_int(stmt, 1);
7639 m_move_from_depth = svn_sqlite__column_int(stmt, 2);
7641 SVN_ERR(svn_sqlite__reset(stmt));
7643 if (! svn_relpath_skip_ancestor(root_relpath, m_from_relpath))
7645 *moved_from_relpath = svn_relpath_join(m_from_relpath, suffix,
7647 *moved_from_op_depth = m_from_op_depth; /* ### Ok? */
7648 return SVN_NO_ERROR;
7650 else if (!m_move_from_depth)
7652 *moved_from_relpath = NULL;
7653 *moved_from_op_depth = -1;
7654 return SVN_NO_ERROR;
7657 return svn_error_trace(
7658 resolve_moved_from(moved_from_relpath,
7659 moved_from_op_depth,
7662 svn_relpath_join(m_from_relpath, suffix,
7665 result_pool, scratch_pool));
7668 static svn_error_t *
7669 delete_node(void *baton,
7670 svn_wc__db_wcroot_t *wcroot,
7671 const char *local_relpath,
7672 apr_pool_t *scratch_pool)
7674 struct op_delete_baton_t *b = baton;
7675 svn_wc__db_status_t status;
7676 svn_boolean_t have_row, op_root;
7677 svn_boolean_t add_work = FALSE;
7678 svn_sqlite__stmt_t *stmt;
7679 int working_op_depth; /* Depth of what is to be deleted */
7680 int keep_op_depth = 0; /* Depth of what is below what is deleted */
7681 svn_node_kind_t kind;
7682 apr_array_header_t *moved_nodes = NULL;
7683 int delete_op_depth = relpath_depth(local_relpath);
7685 assert(*local_relpath); /* Can't delete wcroot */
7687 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7688 STMT_SELECT_NODE_INFO));
7689 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7690 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7694 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
7695 svn_sqlite__reset(stmt),
7696 _("The node '%s' was not found."),
7697 path_for_error_message(wcroot,
7702 working_op_depth = svn_sqlite__column_int(stmt, 0);
7703 status = svn_sqlite__column_token(stmt, 3, presence_map);
7704 kind = svn_sqlite__column_token(stmt, 4, kind_map);
7706 if (working_op_depth < delete_op_depth)
7710 keep_op_depth = working_op_depth;
7716 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7720 svn_wc__db_status_t below_status;
7723 below_op_depth = svn_sqlite__column_int(stmt, 0);
7724 below_status = svn_sqlite__column_token(stmt, 3, presence_map);
7726 if (below_status != svn_wc__db_status_not_present
7727 && below_status != svn_wc__db_status_base_deleted)
7730 keep_op_depth = below_op_depth;
7739 SVN_ERR(svn_sqlite__reset(stmt));
7741 if (working_op_depth != 0) /* WORKING */
7742 SVN_ERR(convert_to_working_status(&status, status));
7744 if (status == svn_wc__db_status_deleted
7745 || status == svn_wc__db_status_not_present)
7746 return SVN_NO_ERROR;
7748 /* Don't copy BASE directories with server excluded nodes */
7749 if (status == svn_wc__db_status_normal && kind == svn_node_dir)
7751 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7752 STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
7753 SVN_ERR(svn_sqlite__bindf(stmt, "is",
7754 wcroot->wc_id, local_relpath));
7755 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7758 const char *absent_path = svn_sqlite__column_text(stmt, 0,
7761 return svn_error_createf(
7762 SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
7763 svn_sqlite__reset(stmt),
7764 _("Cannot delete '%s' as '%s' is excluded by server"),
7765 path_for_error_message(wcroot, local_relpath,
7767 path_for_error_message(wcroot, absent_path,
7770 SVN_ERR(svn_sqlite__reset(stmt));
7772 else if (status == svn_wc__db_status_server_excluded)
7774 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
7775 _("Cannot delete '%s' as it is excluded by server"),
7776 path_for_error_message(wcroot, local_relpath,
7779 else if (status == svn_wc__db_status_excluded)
7781 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
7782 _("Cannot delete '%s' as it is excluded"),
7783 path_for_error_message(wcroot, local_relpath,
7787 if (b->moved_to_relpath)
7789 const char *moved_from_relpath = NULL;
7790 struct moved_node_t *moved_node;
7793 moved_nodes = apr_array_make(scratch_pool, 1,
7794 sizeof(struct moved_node_t *));
7796 /* The node is being moved-away.
7797 * Figure out if the node was moved-here before, or whether this
7798 * is the first time the node is moved. */
7799 if (status == svn_wc__db_status_added)
7800 SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL,
7801 &moved_from_relpath,
7804 wcroot, local_relpath,
7805 scratch_pool, scratch_pool));
7807 if (op_root && moved_from_relpath)
7809 const char *part = svn_relpath_skip_ancestor(local_relpath,
7810 moved_from_relpath);
7812 /* Existing move-root is moved to another location */
7813 moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
7815 moved_node->local_relpath = moved_from_relpath;
7817 moved_node->local_relpath = svn_relpath_join(b->moved_to_relpath,
7818 part, scratch_pool);
7819 moved_node->op_depth = move_op_depth;
7820 moved_node->moved_to_relpath = b->moved_to_relpath;
7821 moved_node->moved_from_depth = -1;
7823 APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
7825 else if (!op_root && (status == svn_wc__db_status_normal
7826 || status == svn_wc__db_status_copied
7827 || status == svn_wc__db_status_moved_here))
7829 /* The node is becoming a move-root for the first time,
7830 * possibly because of a nested move operation. */
7831 moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
7832 moved_node->local_relpath = local_relpath;
7833 moved_node->op_depth = delete_op_depth;
7834 moved_node->moved_to_relpath = b->moved_to_relpath;
7835 moved_node->moved_from_depth = -1;
7837 APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
7839 /* Else: We can't track history of local additions and/or of things we are
7842 /* And update all moved_to values still pointing to this location */
7843 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7844 STMT_UPDATE_MOVED_TO_DESCENDANTS));
7845 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
7847 b->moved_to_relpath));
7848 SVN_ERR(svn_sqlite__update(NULL, stmt));
7851 /* Find children that were moved out of the subtree rooted at this node.
7852 * We'll need to update their op-depth columns because their deletion
7853 * is now implied by the deletion of their parent (i.e. this node). */
7855 apr_pool_t *iterpool;
7858 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7859 STMT_SELECT_MOVED_FOR_DELETE));
7860 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
7863 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7864 iterpool = svn_pool_create(scratch_pool);
7867 struct moved_node_t *mn;
7868 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7869 const char *mv_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
7870 int child_op_depth = svn_sqlite__column_int(stmt, 2);
7871 int moved_from_depth = -1;
7872 svn_boolean_t fixup = FALSE;
7874 if (! b->moved_to_relpath
7875 && ! svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
7877 /* a NULL moved_here_depth will be reported as 0 */
7878 int moved_here_depth = svn_sqlite__column_int(stmt, 3);
7880 /* Plain delete. Fixup move information of descendants that were
7881 moved here, or that were moved out */
7883 if (moved_here_depth >= delete_op_depth)
7885 /* The move we recorded here must be moved to the location
7886 this node had before it was moved here.
7888 This might contain multiple steps when the node was moved
7889 in several places within the to be deleted tree */
7891 /* ### TODO: Add logic */
7893 moved_from_depth = moved_here_depth;
7897 /* Update the op-depth of an moved away node that was
7898 registered as moved by the records that we are about
7901 child_op_depth = delete_op_depth;
7904 else if (b->moved_to_relpath)
7906 /* The node is moved to a new location */
7908 if (delete_op_depth == child_op_depth)
7910 /* Update the op-depth of a tree shadowed by this tree */
7912 /*child_op_depth = delete_depth;*/
7914 else if (child_op_depth >= delete_op_depth
7915 && !svn_relpath_skip_ancestor(local_relpath,
7918 /* Update the move destination of something that is now moved
7921 child_relpath = svn_relpath_skip_ancestor(local_relpath,
7926 child_relpath = svn_relpath_join(b->moved_to_relpath,
7930 if (child_op_depth > delete_op_depth
7931 && svn_relpath_skip_ancestor(local_relpath,
7933 child_op_depth = delete_op_depth;
7936 /* Calculate depth of the shadowing at the new location */
7937 child_op_depth = child_op_depth
7938 - relpath_depth(local_relpath)
7939 + relpath_depth(b->moved_to_relpath);
7949 mn = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
7951 mn->local_relpath = apr_pstrdup(scratch_pool, child_relpath);
7952 mn->moved_to_relpath = apr_pstrdup(scratch_pool, mv_to_relpath);
7953 mn->op_depth = child_op_depth;
7954 mn->moved_from_depth = moved_from_depth;
7957 moved_nodes = apr_array_make(scratch_pool, 1,
7958 sizeof(struct moved_node_t *));
7959 APR_ARRAY_PUSH(moved_nodes, struct moved_node_t *) = mn;
7962 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7964 SVN_ERR(svn_sqlite__reset(stmt));
7966 for (i = 0; moved_nodes && (i < moved_nodes->nelts); i++)
7968 struct moved_node_t *mn = APR_ARRAY_IDX(moved_nodes, i,
7969 struct moved_node_t *);
7971 if (mn->moved_from_depth > 0)
7973 svn_pool_clear(iterpool);
7975 SVN_ERR(resolve_moved_from(&mn->local_relpath, &mn->op_depth,
7976 wcroot, local_relpath,
7978 mn->moved_from_depth,
7979 scratch_pool, iterpool));
7981 if (!mn->local_relpath)
7982 svn_sort__array_delete(moved_nodes, i--, 1);
7986 svn_pool_destroy(iterpool);
7989 if (!b->moved_to_relpath)
7991 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7992 STMT_CLEAR_MOVED_TO_DESCENDANTS));
7993 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7995 SVN_ERR(svn_sqlite__update(NULL, stmt));
7999 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8000 STMT_CLEAR_MOVED_TO_FROM_DEST));
8001 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8004 SVN_ERR(svn_sqlite__update(NULL, stmt));
8009 /* ### Put actual-only nodes into the list? */
8012 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8013 STMT_INSERT_DELETE_LIST));
8014 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
8015 wcroot->wc_id, local_relpath, working_op_depth));
8016 SVN_ERR(svn_sqlite__step_done(stmt));
8019 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8020 STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
8021 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
8022 wcroot->wc_id, local_relpath, delete_op_depth));
8023 SVN_ERR(svn_sqlite__step_done(stmt));
8025 /* Delete ACTUAL_NODE rows, but leave those that have changelist
8027 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8028 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8029 SVN_ERR(svn_sqlite__bindf(stmt, "is",
8030 wcroot->wc_id, local_relpath));
8031 SVN_ERR(svn_sqlite__step_done(stmt));
8033 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8034 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8035 SVN_ERR(svn_sqlite__bindf(stmt, "is",
8036 wcroot->wc_id, local_relpath));
8037 SVN_ERR(svn_sqlite__step_done(stmt));
8039 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8040 STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
8041 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8043 SVN_ERR(svn_sqlite__step_done(stmt));
8047 /* Delete the node at LOCAL_RELPATH, and possibly mark it as moved. */
8049 /* Delete the node and possible descendants. */
8050 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8051 STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
8052 SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
8053 wcroot->wc_id, local_relpath,
8054 keep_op_depth, delete_op_depth));
8055 SVN_ERR(svn_sqlite__step_done(stmt));
8062 for (i = 0; i < moved_nodes->nelts; ++i)
8064 const struct moved_node_t *moved_node
8065 = APR_ARRAY_IDX(moved_nodes, i, void *);
8067 SVN_ERR(delete_update_movedto(wcroot,
8068 moved_node->local_relpath,
8069 moved_node->op_depth,
8070 moved_node->moved_to_relpath,
8075 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8076 STMT_DELETE_FILE_EXTERNALS));
8077 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8078 SVN_ERR(svn_sqlite__step_done(stmt));
8080 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8081 b->delete_dir_externals
8082 ? STMT_DELETE_EXTERNAL_REGISTATIONS
8083 : STMT_DELETE_FILE_EXTERNAL_REGISTATIONS));
8084 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8085 SVN_ERR(svn_sqlite__step_done(stmt));
8087 SVN_ERR(add_work_items(wcroot->sdb, b->work_items, scratch_pool));
8089 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
8090 b->conflict, scratch_pool));
8092 return SVN_NO_ERROR;
8095 static svn_error_t *
8096 op_delete_txn(void *baton,
8097 svn_wc__db_wcroot_t *wcroot,
8098 const char *local_relpath,
8099 apr_pool_t *scratch_pool)
8102 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8103 SVN_ERR(delete_node(baton, wcroot, local_relpath, scratch_pool));
8104 return SVN_NO_ERROR;
8108 struct op_delete_many_baton_t {
8109 apr_array_header_t *rel_targets;
8110 svn_boolean_t delete_dir_externals;
8111 const svn_skel_t *work_items;
8112 } op_delete_many_baton_t;
8114 static svn_error_t *
8115 op_delete_many_txn(void *baton,
8116 svn_wc__db_wcroot_t *wcroot,
8117 const char *local_relpath,
8118 apr_pool_t *scratch_pool)
8120 struct op_delete_many_baton_t *odmb = baton;
8121 struct op_delete_baton_t odb;
8123 apr_pool_t *iterpool;
8125 odb.moved_to_relpath = NULL;
8126 odb.conflict = NULL;
8127 odb.work_items = NULL;
8128 odb.delete_dir_externals = odmb->delete_dir_externals;
8131 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8132 iterpool = svn_pool_create(scratch_pool);
8133 for (i = 0; i < odmb->rel_targets->nelts; i++)
8135 const char *target_relpath = APR_ARRAY_IDX(odmb->rel_targets, i,
8139 svn_pool_clear(iterpool);
8140 SVN_ERR(delete_node(&odb, wcroot, target_relpath, iterpool));
8142 svn_pool_destroy(iterpool);
8144 SVN_ERR(add_work_items(wcroot->sdb, odmb->work_items, scratch_pool));
8146 return SVN_NO_ERROR;
8150 static svn_error_t *
8151 do_delete_notify(void *baton,
8152 svn_wc__db_wcroot_t *wcroot,
8153 svn_cancel_func_t cancel_func,
8155 svn_wc_notify_func2_t notify_func,
8157 apr_pool_t *scratch_pool)
8159 svn_sqlite__stmt_t *stmt;
8160 svn_boolean_t have_row;
8161 apr_pool_t *iterpool;
8163 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8164 STMT_SELECT_DELETE_LIST));
8165 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8167 iterpool = svn_pool_create(scratch_pool);
8170 const char *notify_relpath;
8171 const char *notify_abspath;
8173 svn_pool_clear(iterpool);
8175 notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8176 notify_abspath = svn_dirent_join(wcroot->abspath,
8180 notify_func(notify_baton,
8181 svn_wc_create_notify(notify_abspath,
8182 svn_wc_notify_delete,
8186 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8188 svn_pool_destroy(iterpool);
8190 SVN_ERR(svn_sqlite__reset(stmt));
8192 /* We only allow cancellation after notification for all deleted nodes
8193 * has happened. The nodes are already deleted so we should notify for
8196 SVN_ERR(cancel_func(cancel_baton));
8198 return SVN_NO_ERROR;
8203 svn_wc__db_op_delete(svn_wc__db_t *db,
8204 const char *local_abspath,
8205 const char *moved_to_abspath,
8206 svn_boolean_t delete_dir_externals,
8207 svn_skel_t *conflict,
8208 svn_skel_t *work_items,
8209 svn_cancel_func_t cancel_func,
8211 svn_wc_notify_func2_t notify_func,
8213 apr_pool_t *scratch_pool)
8215 svn_wc__db_wcroot_t *wcroot;
8216 svn_wc__db_wcroot_t *moved_to_wcroot;
8217 const char *local_relpath;
8218 const char *moved_to_relpath;
8219 struct op_delete_baton_t odb;
8221 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8223 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8225 scratch_pool, scratch_pool));
8226 VERIFY_USABLE_WCROOT(wcroot);
8228 if (moved_to_abspath)
8230 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&moved_to_wcroot,
8232 db, moved_to_abspath,
8235 VERIFY_USABLE_WCROOT(moved_to_wcroot);
8237 if (strcmp(wcroot->abspath, moved_to_wcroot->abspath) != 0)
8238 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
8239 _("Cannot move '%s' to '%s' because they "
8240 "are not in the same working copy"),
8241 svn_dirent_local_style(local_abspath,
8243 svn_dirent_local_style(moved_to_abspath,
8247 moved_to_relpath = NULL;
8249 odb.moved_to_relpath = moved_to_relpath;
8250 odb.conflict = conflict;
8251 odb.work_items = work_items;
8252 odb.delete_dir_externals = delete_dir_externals;
8256 /* Perform the deletion operation (transactionally), perform any
8257 notifications necessary, and then clean out our temporary tables. */
8259 SVN_ERR(with_finalization(wcroot, local_relpath,
8260 op_delete_txn, &odb,
8261 do_delete_notify, NULL,
8262 cancel_func, cancel_baton,
8263 notify_func, notify_baton,
8264 STMT_FINALIZE_DELETE,
8269 /* Avoid the trigger work */
8271 SVN_WC__DB_WITH_TXN(
8272 delete_node(&odb, wcroot, local_relpath, scratch_pool),
8276 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
8279 return SVN_NO_ERROR;
8284 svn_wc__db_op_delete_many(svn_wc__db_t *db,
8285 apr_array_header_t *targets,
8286 svn_boolean_t delete_dir_externals,
8287 const svn_skel_t *work_items,
8288 svn_cancel_func_t cancel_func,
8290 svn_wc_notify_func2_t notify_func,
8292 apr_pool_t *scratch_pool)
8294 svn_wc__db_wcroot_t *wcroot;
8295 const char *local_relpath;
8296 struct op_delete_many_baton_t odmb;
8298 apr_pool_t *iterpool;
8300 odmb.rel_targets = apr_array_make(scratch_pool, targets->nelts,
8301 sizeof(const char *));
8302 odmb.work_items = work_items;
8303 odmb.delete_dir_externals = delete_dir_externals;
8304 iterpool = svn_pool_create(scratch_pool);
8305 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8307 APR_ARRAY_IDX(targets, 0,
8309 scratch_pool, iterpool));
8310 VERIFY_USABLE_WCROOT(wcroot);
8311 for (i = 0; i < targets->nelts; i++)
8313 const char *local_abspath = APR_ARRAY_IDX(targets, i, const char*);
8314 svn_wc__db_wcroot_t *target_wcroot;
8316 svn_pool_clear(iterpool);
8318 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&target_wcroot,
8320 APR_ARRAY_IDX(targets, i,
8322 scratch_pool, iterpool));
8323 VERIFY_USABLE_WCROOT(target_wcroot);
8324 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8326 /* Assert that all targets are within the same working copy. */
8327 SVN_ERR_ASSERT(wcroot->wc_id == target_wcroot->wc_id);
8329 APR_ARRAY_PUSH(odmb.rel_targets, const char *) = local_relpath;
8330 SVN_ERR(flush_entries(target_wcroot, local_abspath, svn_depth_infinity,
8334 svn_pool_destroy(iterpool);
8336 /* Perform the deletion operation (transactionally), perform any
8337 notifications necessary, and then clean out our temporary tables. */
8338 return svn_error_trace(with_finalization(wcroot, wcroot->abspath,
8339 op_delete_many_txn, &odmb,
8340 do_delete_notify, NULL,
8341 cancel_func, cancel_baton,
8342 notify_func, notify_baton,
8343 STMT_FINALIZE_DELETE,
8348 /* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of
8349 DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */
8350 static svn_error_t *
8351 read_info(svn_wc__db_status_t *status,
8352 svn_node_kind_t *kind,
8353 svn_revnum_t *revision,
8354 const char **repos_relpath,
8355 apr_int64_t *repos_id,
8356 svn_revnum_t *changed_rev,
8357 apr_time_t *changed_date,
8358 const char **changed_author,
8360 const svn_checksum_t **checksum,
8361 const char **target,
8362 const char **original_repos_relpath,
8363 apr_int64_t *original_repos_id,
8364 svn_revnum_t *original_revision,
8365 svn_wc__db_lock_t **lock,
8366 svn_filesize_t *recorded_size,
8367 apr_time_t *recorded_time,
8368 const char **changelist,
8369 svn_boolean_t *conflicted,
8370 svn_boolean_t *op_root,
8371 svn_boolean_t *had_props,
8372 svn_boolean_t *props_mod,
8373 svn_boolean_t *have_base,
8374 svn_boolean_t *have_more_work,
8375 svn_boolean_t *have_work,
8376 svn_wc__db_wcroot_t *wcroot,
8377 const char *local_relpath,
8378 apr_pool_t *result_pool,
8379 apr_pool_t *scratch_pool)
8381 svn_sqlite__stmt_t *stmt_info;
8382 svn_sqlite__stmt_t *stmt_act;
8383 svn_boolean_t have_info;
8384 svn_boolean_t have_act;
8385 svn_error_t *err = NULL;
8387 /* Obtain the most likely to exist record first, to make sure we don't
8388 have to obtain the SQLite read-lock multiple times */
8389 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
8390 lock ? STMT_SELECT_NODE_INFO_WITH_LOCK
8391 : STMT_SELECT_NODE_INFO));
8392 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
8393 SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
8395 if (changelist || conflicted || props_mod)
8397 SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
8398 STMT_SELECT_ACTUAL_NODE));
8399 SVN_ERR(svn_sqlite__bindf(stmt_act, "is", wcroot->wc_id, local_relpath));
8400 SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
8411 svn_node_kind_t node_kind;
8413 op_depth = svn_sqlite__column_int(stmt_info, 0);
8414 node_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
8418 *status = svn_sqlite__column_token(stmt_info, 3, presence_map);
8420 if (op_depth != 0) /* WORKING */
8421 err = svn_error_compose_create(err,
8422 convert_to_working_status(status,
8432 *repos_id = INVALID_REPOS_ID;
8434 *revision = SVN_INVALID_REVNUM;
8436 /* Our path is implied by our parent somewhere up the tree.
8437 With the NULL value and status, the caller will know to
8438 search up the tree for the base of our path. */
8439 *repos_relpath = NULL;
8443 /* Fetch repository information. If we have a
8444 WORKING_NODE (and have been added), then the repository
8445 we're being added to will be dependent upon a parent. The
8446 caller can scan upwards to locate the repository. */
8447 repos_location_from_columns(repos_id, revision, repos_relpath,
8448 stmt_info, 1, 5, 2, result_pool);
8452 *changed_rev = svn_sqlite__column_revnum(stmt_info, 8);
8456 *changed_date = svn_sqlite__column_int64(stmt_info, 9);
8460 *changed_author = svn_sqlite__column_text(stmt_info, 10,
8465 *recorded_time = svn_sqlite__column_int64(stmt_info, 13);
8469 if (node_kind != svn_node_dir)
8471 *depth = svn_depth_unknown;
8475 *depth = svn_sqlite__column_token_null(stmt_info, 11, depth_map,
8481 if (node_kind != svn_node_file)
8488 err = svn_error_compose_create(
8489 err, svn_sqlite__column_checksum(checksum, stmt_info, 6,
8495 *recorded_size = get_recorded_size(stmt_info, 7);
8499 if (node_kind != svn_node_symlink)
8502 *target = svn_sqlite__column_text(stmt_info, 12, result_pool);
8507 *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8513 if (original_repos_id)
8514 *original_repos_id = INVALID_REPOS_ID;
8515 if (original_revision)
8516 *original_revision = SVN_INVALID_REVNUM;
8517 if (original_repos_relpath)
8518 *original_repos_relpath = NULL;
8522 repos_location_from_columns(original_repos_id,
8524 original_repos_relpath,
8525 stmt_info, 1, 5, 2, result_pool);
8529 *props_mod = have_act && !svn_sqlite__column_is_null(stmt_act, 1);
8533 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt_info, 14);
8540 !svn_sqlite__column_is_null(stmt_act, 2); /* conflict_data */
8543 *conflicted = FALSE;
8551 *lock = lock_from_columns(stmt_info, 17, 18, 19, 20, result_pool);
8555 *have_work = (op_depth != 0);
8559 *op_root = ((op_depth > 0)
8560 && (op_depth == relpath_depth(local_relpath)));
8563 if (have_base || have_more_work)
8566 *have_more_work = FALSE;
8568 while (!err && op_depth != 0)
8570 err = svn_sqlite__step(&have_info, stmt_info);
8572 if (err || !have_info)
8575 op_depth = svn_sqlite__column_int(stmt_info, 0);
8580 *have_more_work = TRUE;
8588 *have_base = (op_depth == 0);
8593 /* A row in ACTUAL_NODE should never exist without a corresponding
8594 node in BASE_NODE and/or WORKING_NODE unless it flags a tree conflict. */
8595 if (svn_sqlite__column_is_null(stmt_act, 2)) /* conflict_data */
8596 err = svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
8597 _("Corrupt data for '%s'"),
8598 path_for_error_message(wcroot, local_relpath,
8600 /* ### What should we return? Should we have a separate
8601 function for reading actual-only nodes? */
8603 /* As a safety measure, until we decide if we want to use
8604 read_info for actual-only nodes, make sure the caller asked
8605 for the conflict status. */
8606 SVN_ERR_ASSERT(conflicted);
8609 *status = svn_wc__db_status_normal; /* What! No it's not! */
8611 *kind = svn_node_unknown;
8613 *revision = SVN_INVALID_REVNUM;
8615 *repos_relpath = NULL;
8617 *repos_id = INVALID_REPOS_ID;
8619 *changed_rev = SVN_INVALID_REVNUM;
8623 *depth = svn_depth_unknown;
8628 if (original_repos_relpath)
8629 *original_repos_relpath = NULL;
8630 if (original_repos_id)
8631 *original_repos_id = INVALID_REPOS_ID;
8632 if (original_revision)
8633 *original_revision = SVN_INVALID_REVNUM;
8641 *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8653 *have_more_work = FALSE;
8659 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
8660 _("The node '%s' was not found."),
8661 path_for_error_message(wcroot, local_relpath,
8665 if (stmt_act != NULL)
8666 err = svn_error_compose_create(err, svn_sqlite__reset(stmt_act));
8668 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
8669 err = svn_error_quick_wrap(err,
8670 apr_psprintf(scratch_pool,
8671 "Error reading node '%s'",
8674 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info)));
8676 return SVN_NO_ERROR;
8681 svn_wc__db_read_info_internal(svn_wc__db_status_t *status,
8682 svn_node_kind_t *kind,
8683 svn_revnum_t *revision,
8684 const char **repos_relpath,
8685 apr_int64_t *repos_id,
8686 svn_revnum_t *changed_rev,
8687 apr_time_t *changed_date,
8688 const char **changed_author,
8690 const svn_checksum_t **checksum,
8691 const char **target,
8692 const char **original_repos_relpath,
8693 apr_int64_t *original_repos_id,
8694 svn_revnum_t *original_revision,
8695 svn_wc__db_lock_t **lock,
8696 svn_filesize_t *recorded_size,
8697 apr_time_t *recorded_time,
8698 const char **changelist,
8699 svn_boolean_t *conflicted,
8700 svn_boolean_t *op_root,
8701 svn_boolean_t *had_props,
8702 svn_boolean_t *props_mod,
8703 svn_boolean_t *have_base,
8704 svn_boolean_t *have_more_work,
8705 svn_boolean_t *have_work,
8706 svn_wc__db_wcroot_t *wcroot,
8707 const char *local_relpath,
8708 apr_pool_t *result_pool,
8709 apr_pool_t *scratch_pool)
8711 return svn_error_trace(
8712 read_info(status, kind, revision, repos_relpath, repos_id,
8713 changed_rev, changed_date, changed_author,
8714 depth, checksum, target, original_repos_relpath,
8715 original_repos_id, original_revision, lock,
8716 recorded_size, recorded_time, changelist, conflicted,
8717 op_root, had_props, props_mod,
8718 have_base, have_more_work, have_work,
8719 wcroot, local_relpath, result_pool, scratch_pool));
8724 svn_wc__db_read_info(svn_wc__db_status_t *status,
8725 svn_node_kind_t *kind,
8726 svn_revnum_t *revision,
8727 const char **repos_relpath,
8728 const char **repos_root_url,
8729 const char **repos_uuid,
8730 svn_revnum_t *changed_rev,
8731 apr_time_t *changed_date,
8732 const char **changed_author,
8734 const svn_checksum_t **checksum,
8735 const char **target,
8736 const char **original_repos_relpath,
8737 const char **original_root_url,
8738 const char **original_uuid,
8739 svn_revnum_t *original_revision,
8740 svn_wc__db_lock_t **lock,
8741 svn_filesize_t *recorded_size,
8742 apr_time_t *recorded_time,
8743 const char **changelist,
8744 svn_boolean_t *conflicted,
8745 svn_boolean_t *op_root,
8746 svn_boolean_t *have_props,
8747 svn_boolean_t *props_mod,
8748 svn_boolean_t *have_base,
8749 svn_boolean_t *have_more_work,
8750 svn_boolean_t *have_work,
8752 const char *local_abspath,
8753 apr_pool_t *result_pool,
8754 apr_pool_t *scratch_pool)
8756 svn_wc__db_wcroot_t *wcroot;
8757 const char *local_relpath;
8758 apr_int64_t repos_id, original_repos_id;
8760 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8762 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
8763 local_abspath, scratch_pool, scratch_pool));
8764 VERIFY_USABLE_WCROOT(wcroot);
8766 SVN_WC__DB_WITH_TXN4(
8767 read_info(status, kind, revision, repos_relpath, &repos_id,
8768 changed_rev, changed_date, changed_author,
8769 depth, checksum, target, original_repos_relpath,
8770 &original_repos_id, original_revision, lock,
8771 recorded_size, recorded_time, changelist, conflicted,
8772 op_root, have_props, props_mod,
8773 have_base, have_more_work, have_work,
8774 wcroot, local_relpath, result_pool, scratch_pool),
8775 svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
8776 wcroot->sdb, repos_id, result_pool),
8777 svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
8778 wcroot->sdb, original_repos_id,
8783 return SVN_NO_ERROR;
8786 static svn_error_t *
8787 is_wclocked(svn_boolean_t *locked,
8788 svn_wc__db_wcroot_t *wcroot,
8789 const char *dir_relpath,
8790 apr_pool_t *scratch_pool);
8792 /* What we really want to store about a node. This relies on the
8793 offset of svn_wc__db_info_t being zero. */
8794 struct read_children_info_item_t
8796 struct svn_wc__db_info_t info;
8801 static svn_error_t *
8802 read_children_info(svn_wc__db_wcroot_t *wcroot,
8803 const char *dir_relpath,
8804 apr_hash_t *conflicts,
8806 apr_pool_t *result_pool,
8807 apr_pool_t *scratch_pool)
8809 svn_sqlite__stmt_t *stmt;
8810 svn_boolean_t have_row;
8811 const char *repos_root_url = NULL;
8812 const char *repos_uuid = NULL;
8813 apr_int64_t last_repos_id = INVALID_REPOS_ID;
8815 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8816 STMT_SELECT_NODE_CHILDREN_INFO));
8817 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
8818 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8822 /* CHILD item points to what we have about the node. We only provide
8823 CHILD->item to our caller. */
8824 struct read_children_info_item_t *child_item;
8825 const char *child_relpath = svn_sqlite__column_text(stmt, 19, NULL);
8826 const char *name = svn_relpath_basename(child_relpath, NULL);
8829 svn_boolean_t new_child;
8831 child_item = svn_hash_gets(nodes, name);
8836 child_item = apr_pcalloc(result_pool, sizeof(*child_item));
8840 op_depth = svn_sqlite__column_int(stmt, 0);
8842 /* Do we have new or better information? */
8843 if (new_child || op_depth > child_item->op_depth)
8845 struct svn_wc__db_info_t *child = &child_item->info;
8846 child_item->op_depth = op_depth;
8848 child->kind = svn_sqlite__column_token(stmt, 4, kind_map);
8850 child->status = svn_sqlite__column_token(stmt, 3, presence_map);
8853 if (child->status == svn_wc__db_status_incomplete)
8854 child->incomplete = TRUE;
8855 err = convert_to_working_status(&child->status, child->status);
8857 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8861 child->revnum = SVN_INVALID_REVNUM;
8863 child->revnum = svn_sqlite__column_revnum(stmt, 5);
8866 child->repos_relpath = NULL;
8868 child->repos_relpath = svn_sqlite__column_text(stmt, 2,
8871 if (op_depth != 0 || svn_sqlite__column_is_null(stmt, 1))
8873 child->repos_root_url = NULL;
8874 child->repos_uuid = NULL;
8878 const char *last_repos_root_url = NULL;
8880 apr_int64_t repos_id = svn_sqlite__column_int64(stmt, 1);
8881 if (!repos_root_url ||
8882 (last_repos_id != INVALID_REPOS_ID &&
8883 repos_id != last_repos_id))
8885 last_repos_root_url = repos_root_url;
8886 err = svn_wc__db_fetch_repos_info(&repos_root_url,
8888 wcroot->sdb, repos_id,
8891 SVN_ERR(svn_error_compose_create(err,
8892 svn_sqlite__reset(stmt)));
8895 if (last_repos_id == INVALID_REPOS_ID)
8896 last_repos_id = repos_id;
8898 /* Assume working copy is all one repos_id so that a
8899 single cached value is sufficient. */
8900 if (repos_id != last_repos_id)
8902 err= svn_error_createf(
8903 SVN_ERR_WC_DB_ERROR, NULL,
8904 _("The node '%s' comes from unexpected repository "
8905 "'%s', expected '%s'; if this node is a file "
8906 "external using the correct URL in the external "
8907 "definition can fix the problem, see issue #4087"),
8908 child_relpath, repos_root_url, last_repos_root_url);
8909 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
8911 child->repos_root_url = repos_root_url;
8912 child->repos_uuid = repos_uuid;
8915 child->changed_rev = svn_sqlite__column_revnum(stmt, 8);
8917 child->changed_date = svn_sqlite__column_int64(stmt, 9);
8919 child->changed_author = svn_sqlite__column_text(stmt, 10,
8922 if (child->kind != svn_node_dir)
8923 child->depth = svn_depth_unknown;
8926 child->depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
8929 SVN_ERR(is_wclocked(&child->locked, wcroot, child_relpath,
8933 child->recorded_time = svn_sqlite__column_int64(stmt, 13);
8934 child->recorded_size = get_recorded_size(stmt, 7);
8935 child->has_checksum = !svn_sqlite__column_is_null(stmt, 6);
8936 child->copied = op_depth > 0 && !svn_sqlite__column_is_null(stmt, 2);
8937 child->had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
8939 if (child->had_props)
8941 apr_hash_t *properties;
8942 err = svn_sqlite__column_properties(&properties, stmt, 14,
8943 scratch_pool, scratch_pool);
8945 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8947 child->special = (child->had_props
8948 && svn_hash_gets(properties, SVN_PROP_SPECIAL));
8952 child->op_root = FALSE;
8954 child->op_root = (op_depth == relpath_depth(child_relpath));
8956 if (op_depth && child->op_root)
8957 child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
8960 svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
8965 child_item->info.have_base = TRUE;
8967 /* Get the lock info, available only at op_depth 0. */
8968 child_item->info.lock = lock_from_columns(stmt, 15, 16, 17, 18,
8971 /* FILE_EXTERNAL flag only on op_depth 0. */
8972 child_item->info.file_external = svn_sqlite__column_boolean(stmt,
8977 const char *moved_to_relpath;
8979 child_item->nr_layers++;
8980 child_item->info.have_more_work = (child_item->nr_layers > 1);
8983 /* A local_relpath can be moved multiple times at different op
8984 depths and it really depends on the caller what is interesting.
8985 We provide a simple linked list with the moved_from information */
8987 moved_to_relpath = svn_sqlite__column_text(stmt, 21, NULL);
8988 if (moved_to_relpath)
8990 struct svn_wc__db_moved_to_info_t *moved_to;
8991 struct svn_wc__db_moved_to_info_t **next;
8992 const char *shadow_op_relpath;
8995 moved_to = apr_pcalloc(result_pool, sizeof(*moved_to));
8996 moved_to->moved_to_abspath = svn_dirent_join(wcroot->abspath,
9000 cur_op_depth = relpath_depth(child_relpath);
9001 shadow_op_relpath = child_relpath;
9003 while (cur_op_depth > op_depth)
9005 shadow_op_relpath = svn_relpath_dirname(shadow_op_relpath,
9010 moved_to->shadow_op_root_abspath =
9011 svn_dirent_join(wcroot->abspath, shadow_op_relpath,
9014 next = &child_item->info.moved_to;
9017 0 < strcmp((*next)->shadow_op_root_abspath,
9018 moved_to->shadow_op_root_abspath))
9019 next = &((*next)->next);
9021 moved_to->next = *next;
9026 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9029 SVN_ERR(svn_sqlite__reset(stmt));
9031 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9032 STMT_SELECT_ACTUAL_CHILDREN_INFO));
9033 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9034 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9038 struct read_children_info_item_t *child_item;
9039 struct svn_wc__db_info_t *child;
9040 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9041 const char *name = svn_relpath_basename(child_relpath, NULL);
9043 child_item = svn_hash_gets(nodes, name);
9046 child_item = apr_pcalloc(result_pool, sizeof(*child_item));
9047 child_item->info.status = svn_wc__db_status_not_present;
9050 child = &child_item->info;
9052 child->changelist = svn_sqlite__column_text(stmt, 1, result_pool);
9054 child->props_mod = !svn_sqlite__column_is_null(stmt, 2);
9056 if (child->props_mod)
9059 apr_hash_t *properties;
9061 err = svn_sqlite__column_properties(&properties, stmt, 2,
9062 scratch_pool, scratch_pool);
9064 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9065 child->special = (NULL != svn_hash_gets(properties,
9070 child->conflicted = !svn_sqlite__column_is_null(stmt, 3); /* conflict */
9072 if (child->conflicted)
9073 svn_hash_sets(conflicts, apr_pstrdup(result_pool, name), "");
9075 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9078 SVN_ERR(svn_sqlite__reset(stmt));
9080 return SVN_NO_ERROR;
9084 svn_wc__db_read_children_info(apr_hash_t **nodes,
9085 apr_hash_t **conflicts,
9087 const char *dir_abspath,
9088 apr_pool_t *result_pool,
9089 apr_pool_t *scratch_pool)
9091 svn_wc__db_wcroot_t *wcroot;
9092 const char *dir_relpath;
9094 *conflicts = apr_hash_make(result_pool);
9095 *nodes = apr_hash_make(result_pool);
9096 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9098 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9100 scratch_pool, scratch_pool));
9101 VERIFY_USABLE_WCROOT(wcroot);
9103 SVN_WC__DB_WITH_TXN(
9104 read_children_info(wcroot, dir_relpath, *conflicts, *nodes,
9105 result_pool, scratch_pool),
9108 return SVN_NO_ERROR;
9111 static svn_error_t *
9112 db_read_props(apr_hash_t **props,
9113 svn_wc__db_wcroot_t *wcroot,
9114 const char *local_relpath,
9115 apr_pool_t *result_pool,
9116 apr_pool_t *scratch_pool);
9118 static svn_error_t *
9119 read_single_info(const struct svn_wc__db_info_t **info,
9120 svn_wc__db_wcroot_t *wcroot,
9121 const char *local_relpath,
9122 apr_pool_t *result_pool,
9123 apr_pool_t *scratch_pool)
9125 struct svn_wc__db_info_t *mtb;
9126 apr_int64_t repos_id;
9127 const svn_checksum_t *checksum;
9128 const char *original_repos_relpath;
9129 svn_boolean_t have_work;
9131 mtb = apr_pcalloc(result_pool, sizeof(*mtb));
9133 SVN_ERR(read_info(&mtb->status, &mtb->kind, &mtb->revnum,
9134 &mtb->repos_relpath, &repos_id, &mtb->changed_rev,
9135 &mtb->changed_date, &mtb->changed_author, &mtb->depth,
9136 &checksum, NULL, &original_repos_relpath, NULL, NULL,
9137 &mtb->lock, &mtb->recorded_size, &mtb->recorded_time,
9138 &mtb->changelist, &mtb->conflicted, &mtb->op_root,
9139 &mtb->had_props, &mtb->props_mod, &mtb->have_base,
9140 &mtb->have_more_work, &have_work,
9141 wcroot, local_relpath,
9142 result_pool, scratch_pool));
9144 /* Query the same rows in the database again for move information */
9145 if (have_work && (mtb->have_base || mtb->have_more_work))
9147 svn_sqlite__stmt_t *stmt;
9148 svn_boolean_t have_row;
9149 const char *cur_relpath = NULL;
9152 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9153 STMT_SELECT_MOVED_TO_NODE));
9154 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9156 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9160 struct svn_wc__db_moved_to_info_t *move;
9161 int op_depth = svn_sqlite__column_int(stmt, 0);
9162 const char *moved_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
9164 move = apr_pcalloc(result_pool, sizeof(*move));
9165 move->moved_to_abspath = svn_dirent_join(wcroot->abspath,
9171 cur_relpath = local_relpath;
9172 cur_op_depth = relpath_depth(cur_relpath);
9174 while (cur_op_depth > op_depth)
9176 cur_relpath = svn_relpath_dirname(cur_relpath, scratch_pool);
9179 move->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath,
9183 move->next = mtb->moved_to;
9184 mtb->moved_to = move;
9186 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9189 SVN_ERR(svn_sqlite__reset(stmt));
9192 /* Maybe we have to get some shadowed lock from BASE to make our test suite
9193 happy... (It might be completely unrelated, but...)
9194 This queries the same BASE row again, joined to the lock table */
9195 if (mtb->have_base && (have_work || mtb->kind == svn_node_file))
9197 svn_boolean_t update_root;
9198 svn_wc__db_lock_t **lock_arg = NULL;
9201 lock_arg = &mtb->lock;
9203 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL, NULL,
9204 NULL, NULL, NULL, NULL, NULL,
9205 NULL, lock_arg, NULL, NULL,
9207 wcroot, local_relpath,
9208 result_pool, scratch_pool));
9210 mtb->file_external = (update_root && mtb->kind == svn_node_file);
9213 if (mtb->status == svn_wc__db_status_added)
9215 svn_wc__db_status_t status;
9217 SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9219 wcroot, local_relpath,
9220 result_pool, scratch_pool));
9222 mtb->moved_here = (status == svn_wc__db_status_moved_here);
9223 mtb->incomplete = (status == svn_wc__db_status_incomplete);
9227 if (mtb->kind == svn_node_file
9228 && (mtb->had_props || mtb->props_mod))
9230 apr_hash_t *properties;
9233 SVN_ERR(db_read_props(&properties,
9234 wcroot, local_relpath,
9235 scratch_pool, scratch_pool));
9237 SVN_ERR(db_read_pristine_props(&properties, wcroot, local_relpath,
9238 TRUE /* deleted_ok */,
9239 scratch_pool, scratch_pool));
9241 mtb->special = (NULL != svn_hash_gets(properties, SVN_PROP_SPECIAL));
9245 mtb->has_checksum = (checksum != NULL);
9246 mtb->copied = (original_repos_relpath != NULL);
9248 SVN_ERR(svn_wc__db_fetch_repos_info(&mtb->repos_root_url, &mtb->repos_uuid,
9249 wcroot->sdb, repos_id, result_pool));
9251 if (mtb->kind == svn_node_dir)
9252 SVN_ERR(is_wclocked(&mtb->locked, wcroot, local_relpath, scratch_pool));
9256 return SVN_NO_ERROR;
9260 svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info,
9262 const char *local_abspath,
9263 apr_pool_t *result_pool,
9264 apr_pool_t *scratch_pool)
9266 svn_wc__db_wcroot_t *wcroot;
9267 const char *local_relpath;
9269 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9271 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9273 scratch_pool, scratch_pool));
9274 VERIFY_USABLE_WCROOT(wcroot);
9276 SVN_WC__DB_WITH_TXN(read_single_info(info, wcroot, local_relpath,
9277 result_pool, scratch_pool),
9280 return SVN_NO_ERROR;
9284 svn_wc__db_read_pristine_info(svn_wc__db_status_t *status,
9285 svn_node_kind_t *kind,
9286 svn_revnum_t *changed_rev,
9287 apr_time_t *changed_date,
9288 const char **changed_author,
9289 svn_depth_t *depth, /* dirs only */
9290 const svn_checksum_t **checksum, /* files only */
9291 const char **target, /* symlinks only */
9292 svn_boolean_t *had_props,
9295 const char *local_abspath,
9296 apr_pool_t *result_pool,
9297 apr_pool_t *scratch_pool)
9299 svn_wc__db_wcroot_t *wcroot;
9300 const char *local_relpath;
9301 svn_sqlite__stmt_t *stmt;
9302 svn_boolean_t have_row;
9303 svn_error_t *err = NULL;
9305 svn_wc__db_status_t raw_status;
9306 svn_node_kind_t node_kind;
9308 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9310 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9312 scratch_pool, scratch_pool));
9313 VERIFY_USABLE_WCROOT(wcroot);
9315 /* Obtain the most likely to exist record first, to make sure we don't
9316 have to obtain the SQLite read-lock multiple times */
9317 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9318 STMT_SELECT_NODE_INFO));
9319 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9320 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9324 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9325 svn_sqlite__reset(stmt),
9326 _("The node '%s' was not found."),
9327 path_for_error_message(wcroot,
9332 op_depth = svn_sqlite__column_int(stmt, 0);
9333 raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9335 if (op_depth > 0 && raw_status == svn_wc__db_status_base_deleted)
9337 SVN_ERR(svn_sqlite__step_row(stmt));
9339 op_depth = svn_sqlite__column_int(stmt, 0);
9340 raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9343 node_kind = svn_sqlite__column_token(stmt, 4, kind_map);
9349 err = svn_error_compose_create(err,
9350 convert_to_working_status(
9355 *status = raw_status;
9363 *changed_rev = svn_sqlite__column_revnum(stmt, 8);
9367 *changed_date = svn_sqlite__column_int64(stmt, 9);
9371 *changed_author = svn_sqlite__column_text(stmt, 10,
9376 if (node_kind != svn_node_dir)
9378 *depth = svn_depth_unknown;
9382 *depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
9388 if (node_kind != svn_node_file)
9395 err2 = svn_sqlite__column_checksum(checksum, stmt, 6, result_pool);
9400 err = svn_error_compose_create(
9404 _("The node '%s' has a corrupt checksum value."),
9405 path_for_error_message(wcroot, local_relpath,
9414 if (node_kind != svn_node_symlink)
9417 *target = svn_sqlite__column_text(stmt, 12, result_pool);
9421 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
9425 if (raw_status == svn_wc__db_status_normal
9426 || raw_status == svn_wc__db_status_incomplete)
9428 SVN_ERR(svn_sqlite__column_properties(props, stmt, 14,
9429 result_pool, scratch_pool));
9431 *props = apr_hash_make(result_pool);
9435 assert(svn_sqlite__column_is_null(stmt, 14));
9440 return svn_error_trace(
9441 svn_error_compose_create(err,
9442 svn_sqlite__reset(stmt)));
9446 svn_wc__db_read_children_walker_info(apr_hash_t **nodes,
9448 const char *dir_abspath,
9449 apr_pool_t *result_pool,
9450 apr_pool_t *scratch_pool)
9452 svn_wc__db_wcroot_t *wcroot;
9453 const char *dir_relpath;
9454 svn_sqlite__stmt_t *stmt;
9455 svn_boolean_t have_row;
9457 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9459 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9461 scratch_pool, scratch_pool));
9462 VERIFY_USABLE_WCROOT(wcroot);
9464 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9465 STMT_SELECT_NODE_CHILDREN_WALKER_INFO));
9466 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9467 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9469 *nodes = apr_hash_make(result_pool);
9472 struct svn_wc__db_walker_info_t *child;
9473 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9474 const char *name = svn_relpath_basename(child_relpath, NULL);
9475 int op_depth = svn_sqlite__column_int(stmt, 1);
9478 child = apr_palloc(result_pool, sizeof(*child));
9479 child->status = svn_sqlite__column_token(stmt, 2, presence_map);
9482 err = convert_to_working_status(&child->status, child->status);
9484 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9486 child->kind = svn_sqlite__column_token(stmt, 3, kind_map);
9487 svn_hash_sets(*nodes, apr_pstrdup(result_pool, name), child);
9489 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9492 SVN_ERR(svn_sqlite__reset(stmt));
9494 return SVN_NO_ERROR;
9498 svn_wc__db_read_node_install_info(const char **wcroot_abspath,
9499 const svn_checksum_t **sha1_checksum,
9500 apr_hash_t **pristine_props,
9501 apr_time_t *changed_date,
9503 const char *local_abspath,
9504 const char *wri_abspath,
9505 apr_pool_t *result_pool,
9506 apr_pool_t *scratch_pool)
9508 svn_wc__db_wcroot_t *wcroot;
9509 const char *local_relpath;
9510 svn_sqlite__stmt_t *stmt;
9511 svn_error_t *err = NULL;
9512 svn_boolean_t have_row;
9514 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9517 wri_abspath = local_abspath;
9519 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9520 wri_abspath, scratch_pool, scratch_pool));
9521 VERIFY_USABLE_WCROOT(wcroot);
9523 if (local_abspath != wri_abspath
9524 && strcmp(local_abspath, wri_abspath))
9526 if (!svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
9527 return svn_error_createf(
9528 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
9529 _("The node '%s' is not in working copy '%s'"),
9530 svn_dirent_local_style(local_abspath, scratch_pool),
9531 svn_dirent_local_style(wcroot->abspath, scratch_pool));
9533 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
9536 if (wcroot_abspath != NULL)
9537 *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
9539 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9540 STMT_SELECT_NODE_INFO));
9542 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9544 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9548 if (!err && sha1_checksum)
9549 err = svn_sqlite__column_checksum(sha1_checksum, stmt, 6, result_pool);
9551 if (!err && pristine_props)
9553 err = svn_sqlite__column_properties(pristine_props, stmt, 14,
9554 result_pool, scratch_pool);
9555 /* Null means no props (assuming presence normal or incomplete). */
9556 if (*pristine_props == NULL)
9557 *pristine_props = apr_hash_make(result_pool);
9561 *changed_date = svn_sqlite__column_int64(stmt, 9);
9564 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9565 svn_sqlite__reset(stmt),
9566 _("The node '%s' is not installable"),
9567 svn_dirent_local_style(local_abspath,
9570 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9572 return SVN_NO_ERROR;
9577 /* The body of svn_wc__db_read_url().
9579 static svn_error_t *
9580 read_url_txn(const char **url,
9581 svn_wc__db_wcroot_t *wcroot,
9582 const char *local_relpath,
9583 apr_pool_t *result_pool,
9584 apr_pool_t *scratch_pool)
9586 svn_wc__db_status_t status;
9587 const char *repos_relpath;
9588 const char *repos_root_url;
9589 apr_int64_t repos_id;
9590 svn_boolean_t have_base;
9592 SVN_ERR(read_info(&status, NULL, NULL, &repos_relpath, &repos_id, NULL,
9593 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9594 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9595 &have_base, NULL, NULL,
9596 wcroot, local_relpath, scratch_pool, scratch_pool));
9598 if (repos_relpath == NULL)
9600 if (status == svn_wc__db_status_added)
9602 SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id, NULL,
9603 NULL, NULL, NULL, NULL, NULL,
9604 wcroot, local_relpath,
9605 scratch_pool, scratch_pool));
9607 else if (status == svn_wc__db_status_deleted)
9609 const char *base_del_relpath;
9610 const char *work_del_relpath;
9612 SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
9619 if (base_del_relpath)
9621 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
9626 NULL, NULL, NULL, NULL,
9632 repos_relpath = svn_relpath_join(
9634 svn_dirent_skip_ancestor(base_del_relpath,
9640 /* The parent of the WORKING delete, must be an addition */
9641 const char *work_relpath = NULL;
9643 /* work_del_relpath should not be NULL. However, we have
9644 * observed instances where that assumption was not met.
9645 * Bail out in that case instead of crashing with a segfault.
9647 SVN_ERR_ASSERT(work_del_relpath != NULL);
9648 work_relpath = svn_relpath_dirname(work_del_relpath,
9651 SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id,
9652 NULL, NULL, NULL, NULL, NULL, NULL,
9653 wcroot, work_relpath,
9654 scratch_pool, scratch_pool));
9656 repos_relpath = svn_relpath_join(
9658 svn_dirent_skip_ancestor(work_relpath,
9663 else if (status == svn_wc__db_status_excluded)
9665 const char *parent_relpath;
9669 /* Set 'url' to the *full URL* of the parent WC dir,
9670 * and 'name' to the *single path component* that is the
9671 * basename of this WC directory, so that joining them will result
9672 * in the correct full URL. */
9673 svn_relpath_split(&parent_relpath, &name, local_relpath,
9675 SVN_ERR(read_url_txn(&url2, wcroot, parent_relpath,
9676 scratch_pool, scratch_pool));
9678 *url = svn_path_url_add_component2(url2, name, result_pool);
9680 return SVN_NO_ERROR;
9684 /* All working statee are explicitly handled and all base statee
9685 have a repos_relpath */
9686 SVN_ERR_MALFUNCTION();
9690 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot->sdb,
9691 repos_id, scratch_pool));
9693 SVN_ERR_ASSERT(repos_root_url != NULL && repos_relpath != NULL);
9694 *url = svn_path_url_add_component2(repos_root_url, repos_relpath,
9697 return SVN_NO_ERROR;
9702 svn_wc__db_read_url(const char **url,
9704 const char *local_abspath,
9705 apr_pool_t *result_pool,
9706 apr_pool_t *scratch_pool)
9708 svn_wc__db_wcroot_t *wcroot;
9709 const char *local_relpath;
9711 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9713 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9715 scratch_pool, scratch_pool));
9716 VERIFY_USABLE_WCROOT(wcroot);
9718 SVN_WC__DB_WITH_TXN(read_url_txn(url, wcroot, local_relpath,
9719 result_pool, scratch_pool),
9722 return SVN_NO_ERROR;
9726 /* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
9727 a hash table mapping <tt>char *</tt> names onto svn_string_t *
9728 values for any properties of immediate or recursive child nodes of
9729 LOCAL_ABSPATH, the actual query being determined by STMT_IDX.
9730 If FILES_ONLY is true, only report properties for file child nodes.
9731 Check for cancellation between calls of RECEIVER_FUNC.
9733 typedef struct cache_props_baton_t
9736 svn_boolean_t pristine;
9737 const apr_array_header_t *changelists;
9738 svn_cancel_func_t cancel_func;
9740 } cache_props_baton_t;
9743 static svn_error_t *
9744 cache_props_recursive(void *cb_baton,
9745 svn_wc__db_wcroot_t *wcroot,
9746 const char *local_relpath,
9747 apr_pool_t *scratch_pool)
9749 cache_props_baton_t *baton = cb_baton;
9750 svn_sqlite__stmt_t *stmt;
9753 SVN_ERR(populate_targets_tree(wcroot, local_relpath, baton->depth,
9754 baton->changelists, scratch_pool));
9756 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
9757 STMT_CREATE_TARGET_PROP_CACHE));
9759 if (baton->pristine)
9760 stmt_idx = STMT_CACHE_TARGET_PRISTINE_PROPS;
9762 stmt_idx = STMT_CACHE_TARGET_PROPS;
9764 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
9765 SVN_ERR(svn_sqlite__bind_int64(stmt, 1, wcroot->wc_id));
9766 SVN_ERR(svn_sqlite__step_done(stmt));
9768 return SVN_NO_ERROR;
9773 svn_wc__db_read_props_streamily(svn_wc__db_t *db,
9774 const char *local_abspath,
9776 svn_boolean_t pristine,
9777 const apr_array_header_t *changelists,
9778 svn_wc__proplist_receiver_t receiver_func,
9779 void *receiver_baton,
9780 svn_cancel_func_t cancel_func,
9782 apr_pool_t *scratch_pool)
9784 svn_wc__db_wcroot_t *wcroot;
9785 const char *local_relpath;
9786 svn_sqlite__stmt_t *stmt;
9787 cache_props_baton_t baton;
9788 svn_boolean_t have_row;
9789 apr_pool_t *iterpool;
9790 svn_error_t *err = NULL;
9792 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9793 SVN_ERR_ASSERT(receiver_func);
9794 SVN_ERR_ASSERT((depth == svn_depth_files) ||
9795 (depth == svn_depth_immediates) ||
9796 (depth == svn_depth_infinity));
9798 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
9800 scratch_pool, scratch_pool));
9801 VERIFY_USABLE_WCROOT(wcroot);
9803 baton.depth = depth;
9804 baton.pristine = pristine;
9805 baton.changelists = changelists;
9806 baton.cancel_func = cancel_func;
9807 baton.cancel_baton = cancel_baton;
9809 SVN_ERR(with_finalization(wcroot, local_relpath,
9810 cache_props_recursive, &baton,
9812 cancel_func, cancel_baton,
9814 STMT_DROP_TARGETS_LIST,
9817 iterpool = svn_pool_create(scratch_pool);
9819 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9820 STMT_SELECT_ALL_TARGET_PROP_CACHE));
9821 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9822 while (!err && have_row)
9826 svn_pool_clear(iterpool);
9828 SVN_ERR(svn_sqlite__column_properties(&props, stmt, 1, iterpool,
9831 /* see if someone wants to cancel this operation. */
9833 err = cancel_func(cancel_baton);
9835 if (!err && props && apr_hash_count(props) != 0)
9837 const char *child_relpath;
9838 const char *child_abspath;
9840 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9841 child_abspath = svn_dirent_join(wcroot->abspath,
9842 child_relpath, iterpool);
9844 err = receiver_func(receiver_baton, child_abspath, props, iterpool);
9847 err = svn_error_compose_create(err, svn_sqlite__step(&have_row, stmt));
9850 err = svn_error_compose_create(err, svn_sqlite__reset(stmt));
9852 svn_pool_destroy(iterpool);
9854 SVN_ERR(svn_error_compose_create(
9856 svn_sqlite__exec_statements(wcroot->sdb,
9857 STMT_DROP_TARGET_PROP_CACHE)));
9858 return SVN_NO_ERROR;
9862 /* Helper for svn_wc__db_read_props().
9864 static svn_error_t *
9865 db_read_props(apr_hash_t **props,
9866 svn_wc__db_wcroot_t *wcroot,
9867 const char *local_relpath,
9868 apr_pool_t *result_pool,
9869 apr_pool_t *scratch_pool)
9871 svn_sqlite__stmt_t *stmt;
9872 svn_boolean_t have_row;
9873 svn_error_t *err = NULL;
9875 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9876 STMT_SELECT_ACTUAL_PROPS));
9877 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9878 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9880 if (have_row && !svn_sqlite__column_is_null(stmt, 0))
9882 err = svn_sqlite__column_properties(props, stmt, 0,
9883 result_pool, scratch_pool);
9888 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9891 return SVN_NO_ERROR;
9893 /* No local changes. Return the pristine props for this node. */
9894 SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, FALSE,
9895 result_pool, scratch_pool));
9898 /* Pristine properties are not defined for this node.
9899 ### we need to determine whether this node is in a state that
9900 ### allows for ACTUAL properties (ie. not deleted). for now,
9901 ### just say all nodes, no matter the state, have at least an
9902 ### empty set of props. */
9903 *props = apr_hash_make(result_pool);
9906 return SVN_NO_ERROR;
9911 svn_wc__db_read_props(apr_hash_t **props,
9913 const char *local_abspath,
9914 apr_pool_t *result_pool,
9915 apr_pool_t *scratch_pool)
9917 svn_wc__db_wcroot_t *wcroot;
9918 const char *local_relpath;
9920 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9922 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9923 local_abspath, scratch_pool, scratch_pool));
9924 VERIFY_USABLE_WCROOT(wcroot);
9926 SVN_WC__DB_WITH_TXN(db_read_props(props, wcroot, local_relpath,
9927 result_pool, scratch_pool),
9930 return SVN_NO_ERROR;
9934 static svn_error_t *
9935 db_read_pristine_props(apr_hash_t **props,
9936 svn_wc__db_wcroot_t *wcroot,
9937 const char *local_relpath,
9938 svn_boolean_t deleted_ok,
9939 apr_pool_t *result_pool,
9940 apr_pool_t *scratch_pool)
9942 svn_sqlite__stmt_t *stmt;
9943 svn_boolean_t have_row;
9944 svn_wc__db_status_t presence;
9946 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_NODE_PROPS));
9947 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9949 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9953 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9954 svn_sqlite__reset(stmt),
9955 _("The node '%s' was not found."),
9956 path_for_error_message(wcroot,
9962 /* Examine the presence: */
9963 presence = svn_sqlite__column_token(stmt, 1, presence_map);
9965 /* For "base-deleted", it is obvious the pristine props are located
9966 below the current node. Fetch the NODE from the next record. */
9967 if (presence == svn_wc__db_status_base_deleted && deleted_ok)
9969 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9971 SVN_ERR_ASSERT(have_row);
9973 presence = svn_sqlite__column_token(stmt, 1, presence_map);
9976 /* normal or copied: Fetch properties (during update we want
9977 properties for incomplete as well) */
9978 if (presence == svn_wc__db_status_normal
9979 || presence == svn_wc__db_status_incomplete)
9983 err = svn_sqlite__column_properties(props, stmt, 0, result_pool,
9985 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9988 *props = apr_hash_make(result_pool);
9990 return SVN_NO_ERROR;
9993 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
9994 svn_sqlite__reset(stmt),
9995 _("The node '%s' has a status that"
9996 " has no properties."),
9997 path_for_error_message(wcroot,
10004 svn_wc__db_read_pristine_props(apr_hash_t **props,
10006 const char *local_abspath,
10007 apr_pool_t *result_pool,
10008 apr_pool_t *scratch_pool)
10010 svn_wc__db_wcroot_t *wcroot;
10011 const char *local_relpath;
10013 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10015 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10016 local_abspath, scratch_pool, scratch_pool));
10017 VERIFY_USABLE_WCROOT(wcroot);
10019 SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, TRUE,
10020 result_pool, scratch_pool));
10021 return SVN_NO_ERROR;
10025 svn_wc__db_prop_retrieve_recursive(apr_hash_t **values,
10027 const char *local_abspath,
10028 const char *propname,
10029 apr_pool_t *result_pool,
10030 apr_pool_t *scratch_pool)
10032 svn_wc__db_wcroot_t *wcroot;
10033 const char *local_relpath;
10034 svn_sqlite__stmt_t *stmt;
10035 svn_boolean_t have_row;
10036 apr_pool_t *iterpool;
10038 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10040 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10041 local_abspath, scratch_pool, scratch_pool));
10042 VERIFY_USABLE_WCROOT(wcroot);
10044 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10045 STMT_SELECT_CURRENT_PROPS_RECURSIVE));
10046 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10048 *values = apr_hash_make(result_pool);
10050 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10051 iterpool = svn_pool_create(scratch_pool);
10054 apr_hash_t *node_props;
10055 svn_string_t *value;
10057 svn_pool_clear(iterpool);
10059 SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 0,
10060 iterpool, iterpool));
10062 value = (node_props
10063 ? svn_hash_gets(node_props, propname)
10068 svn_hash_sets(*values,
10069 svn_dirent_join(wcroot->abspath,
10070 svn_sqlite__column_text(stmt, 1, NULL),
10072 svn_string_dup(value, result_pool));
10075 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10078 svn_pool_destroy(iterpool);
10080 return svn_error_trace(svn_sqlite__reset(stmt));
10083 /* The body of svn_wc__db_read_cached_iprops(). */
10084 static svn_error_t *
10085 db_read_cached_iprops(apr_array_header_t **iprops,
10086 svn_wc__db_wcroot_t *wcroot,
10087 const char *local_relpath,
10088 apr_pool_t *result_pool,
10089 apr_pool_t *scratch_pool)
10091 svn_sqlite__stmt_t *stmt;
10092 svn_boolean_t have_row;
10094 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_IPROPS));
10095 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10096 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10100 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10101 svn_sqlite__reset(stmt),
10102 _("The node '%s' was not found."),
10103 path_for_error_message(wcroot, local_relpath,
10107 SVN_ERR(svn_sqlite__column_iprops(iprops, stmt, 0,
10108 result_pool, scratch_pool));
10110 SVN_ERR(svn_sqlite__reset(stmt));
10112 return SVN_NO_ERROR;
10116 svn_wc__db_read_cached_iprops(apr_array_header_t **iprops,
10118 const char *local_abspath,
10119 apr_pool_t *result_pool,
10120 apr_pool_t *scratch_pool)
10122 svn_wc__db_wcroot_t *wcroot;
10123 const char *local_relpath;
10125 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10127 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10129 scratch_pool, scratch_pool));
10130 VERIFY_USABLE_WCROOT(wcroot);
10132 /* Don't use with_txn yet, as we perform just a single transaction */
10133 SVN_ERR(db_read_cached_iprops(iprops, wcroot, local_relpath,
10134 result_pool, scratch_pool));
10138 *iprops = apr_array_make(result_pool, 0,
10139 sizeof(svn_prop_inherited_item_t *));
10142 return SVN_NO_ERROR;
10145 /* Remove all prop name value pairs from PROP_HASH where the property
10146 name is not PROPNAME. */
10148 filter_unwanted_props(apr_hash_t *prop_hash,
10149 const char * propname,
10150 apr_pool_t *scratch_pool)
10152 apr_hash_index_t *hi;
10154 for (hi = apr_hash_first(scratch_pool, prop_hash);
10156 hi = apr_hash_next(hi))
10158 const char *ipropname = svn__apr_hash_index_key(hi);
10160 if (strcmp(ipropname, propname) != 0)
10161 svn_hash_sets(prop_hash, ipropname, NULL);
10166 /* Get the changed properties as stored in the ACTUAL table */
10167 static svn_error_t *
10168 db_get_changed_props(apr_hash_t **actual_props,
10169 svn_wc__db_wcroot_t *wcroot,
10170 const char *local_relpath,
10171 apr_pool_t *result_pool,
10172 apr_pool_t *scratch_pool)
10174 svn_sqlite__stmt_t *stmt;
10175 svn_boolean_t have_row;
10176 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10177 STMT_SELECT_ACTUAL_PROPS));
10178 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10179 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10181 if (have_row && !svn_sqlite__column_is_null(stmt, 0))
10182 SVN_ERR(svn_sqlite__column_properties(actual_props, stmt, 0,
10183 result_pool, scratch_pool));
10185 *actual_props = NULL; /* Cached when we read that record */
10187 return svn_error_trace(svn_sqlite__reset(stmt));
10190 /* The body of svn_wc__db_read_inherited_props(). */
10191 static svn_error_t *
10192 db_read_inherited_props(apr_array_header_t **inherited_props,
10193 apr_hash_t **actual_props,
10194 svn_wc__db_wcroot_t *wcroot,
10195 const char *local_relpath,
10196 const char *propname,
10197 apr_pool_t *result_pool,
10198 apr_pool_t *scratch_pool)
10201 apr_array_header_t *cached_iprops = NULL;
10202 apr_array_header_t *iprops;
10203 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
10204 svn_sqlite__stmt_t *stmt;
10205 const char *relpath;
10206 const char *expected_parent_repos_relpath = NULL;
10207 const char *parent_relpath;
10209 iprops = apr_array_make(result_pool, 1,
10210 sizeof(svn_prop_inherited_item_t *));
10211 *inherited_props = iprops;
10214 *actual_props = NULL;
10216 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10217 STMT_SELECT_NODE_INFO));
10219 relpath = local_relpath;
10221 /* Walk up to the root of the WC looking for inherited properties. When we
10222 reach the WC root also check for cached inherited properties. */
10223 for (relpath = local_relpath; relpath; relpath = parent_relpath)
10225 svn_boolean_t have_row;
10227 svn_wc__db_status_t status;
10228 apr_hash_t *node_props;
10230 parent_relpath = relpath[0] ? svn_relpath_dirname(relpath, scratch_pool)
10233 svn_pool_clear(iterpool);
10235 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, relpath));
10237 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10240 return svn_error_createf(
10241 SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
10242 _("The node '%s' was not found."),
10243 path_for_error_message(wcroot, relpath,
10246 op_depth = svn_sqlite__column_int(stmt, 0);
10248 status = svn_sqlite__column_token(stmt, 3, presence_map);
10250 if (status != svn_wc__db_status_normal
10251 && status != svn_wc__db_status_incomplete)
10252 return svn_error_createf(
10253 SVN_ERR_WC_PATH_UNEXPECTED_STATUS, svn_sqlite__reset(stmt),
10254 _("The node '%s' has a status that has no properties."),
10255 path_for_error_message(wcroot, relpath,
10260 /* WORKING node. Nothing to check */
10262 else if (expected_parent_repos_relpath)
10264 const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10266 if (strcmp(expected_parent_repos_relpath, repos_relpath) != 0)
10268 /* The child of this node has a different parent than this node
10269 (It is "switched"), so we can stop here. Note that switched
10270 with the same parent is not interesting for us here. */
10271 SVN_ERR(svn_sqlite__reset(stmt));
10275 expected_parent_repos_relpath =
10276 svn_relpath_dirname(expected_parent_repos_relpath, scratch_pool);
10280 const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10282 expected_parent_repos_relpath =
10283 svn_relpath_dirname(repos_relpath, scratch_pool);
10287 && !svn_sqlite__column_is_null(stmt, 16))
10289 /* The node contains a cache. No reason to look further */
10290 SVN_ERR(svn_sqlite__column_iprops(&cached_iprops, stmt, 16,
10291 result_pool, iterpool));
10293 parent_relpath = NULL; /* Stop after this */
10296 SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 14,
10297 iterpool, iterpool));
10299 SVN_ERR(svn_sqlite__reset(stmt));
10301 /* If PARENT_ABSPATH is a parent of LOCAL_ABSPATH, then LOCAL_ABSPATH
10302 can inherit properties from it. */
10303 if (relpath != local_relpath)
10305 apr_hash_t *changed_props;
10307 SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10308 result_pool, iterpool));
10311 node_props = changed_props;
10312 else if (node_props)
10313 node_props = svn_prop_hash_dup(node_props, result_pool);
10315 if (node_props && apr_hash_count(node_props))
10317 /* If we only want PROPNAME filter out any other properties. */
10319 filter_unwanted_props(node_props, propname, iterpool);
10321 if (apr_hash_count(node_props))
10323 svn_prop_inherited_item_t *iprop_elt =
10324 apr_pcalloc(result_pool,
10325 sizeof(svn_prop_inherited_item_t));
10326 iprop_elt->path_or_url = svn_dirent_join(wcroot->abspath,
10330 iprop_elt->prop_hash = node_props;
10331 /* Build the output array in depth-first order. */
10332 svn_sort__array_insert(&iprop_elt, iprops, 0);
10336 else if (actual_props)
10338 apr_hash_t *changed_props;
10340 SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10341 result_pool, iterpool));
10344 *actual_props = changed_props;
10345 else if (node_props)
10346 *actual_props = svn_prop_hash_dup(node_props, result_pool);
10352 for (i = cached_iprops->nelts - 1; i >= 0; i--)
10354 svn_prop_inherited_item_t *cached_iprop =
10355 APR_ARRAY_IDX(cached_iprops, i, svn_prop_inherited_item_t *);
10357 /* An empty property hash in the iprops cache means there are no
10358 inherited properties. */
10359 if (apr_hash_count(cached_iprop->prop_hash) == 0)
10363 filter_unwanted_props(cached_iprop->prop_hash, propname,
10366 /* If we didn't filter everything then keep this iprop. */
10367 if (apr_hash_count(cached_iprop->prop_hash))
10368 svn_sort__array_insert(&cached_iprop, iprops, 0);
10372 if (actual_props && !*actual_props)
10373 *actual_props = apr_hash_make(result_pool);
10375 svn_pool_destroy(iterpool);
10376 return SVN_NO_ERROR;
10380 svn_wc__db_read_inherited_props(apr_array_header_t **iprops,
10381 apr_hash_t **actual_props,
10383 const char *local_abspath,
10384 const char *propname,
10385 apr_pool_t *result_pool,
10386 apr_pool_t *scratch_pool)
10388 svn_wc__db_wcroot_t *wcroot;
10389 const char *local_relpath;
10391 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10393 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10395 scratch_pool, scratch_pool));
10396 VERIFY_USABLE_WCROOT(wcroot);
10398 SVN_WC__DB_WITH_TXN(db_read_inherited_props(iprops, actual_props,
10399 wcroot, local_relpath, propname,
10400 result_pool, scratch_pool),
10403 return SVN_NO_ERROR;
10406 /* The body of svn_wc__db_get_children_with_cached_iprops().
10408 static svn_error_t *
10409 get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10410 svn_wc__db_wcroot_t *wcroot,
10411 const char *local_relpath,
10413 apr_pool_t *result_pool,
10414 apr_pool_t *scratch_pool)
10416 svn_sqlite__stmt_t *stmt;
10417 svn_boolean_t have_row;
10419 *iprop_paths = apr_hash_make(result_pool);
10421 /* First check if LOCAL_RELPATH itself has iprops */
10422 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10423 STMT_SELECT_IPROPS_NODE));
10424 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10425 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10429 const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10431 const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10432 relpath_with_cache,
10434 svn_hash_sets(*iprop_paths, abspath_with_cache,
10435 svn_sqlite__column_text(stmt, 1, result_pool));
10437 SVN_ERR(svn_sqlite__reset(stmt));
10439 if (depth == svn_depth_empty)
10440 return SVN_NO_ERROR;
10442 /* Now fetch information for children or all descendants */
10443 if (depth == svn_depth_files
10444 || depth == svn_depth_immediates)
10446 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10447 STMT_SELECT_IPROPS_CHILDREN));
10449 else /* Default to svn_depth_infinity. */
10451 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10452 STMT_SELECT_IPROPS_RECURSIVE));
10455 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10456 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10460 const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10462 const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10463 relpath_with_cache,
10465 svn_hash_sets(*iprop_paths, abspath_with_cache,
10466 svn_sqlite__column_text(stmt, 1, result_pool));
10467 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10470 SVN_ERR(svn_sqlite__reset(stmt));
10472 /* For depth files we should filter non files */
10473 if (depth == svn_depth_files)
10475 apr_hash_index_t *hi;
10476 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
10478 for (hi = apr_hash_first(scratch_pool, *iprop_paths);
10480 hi = apr_hash_next(hi))
10482 const char *child_abspath = svn__apr_hash_index_key(hi);
10483 const char *child_relpath;
10484 svn_node_kind_t child_kind;
10486 svn_pool_clear(iterpool);
10488 child_relpath = svn_dirent_is_child(local_relpath, child_abspath,
10491 if (! child_relpath)
10493 continue; /* local_relpath itself */
10496 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &child_kind, NULL,
10497 NULL, NULL, NULL, NULL,
10498 NULL, NULL, NULL, NULL,
10499 NULL, NULL, NULL, NULL,
10500 wcroot, child_relpath,
10504 /* Filter if not a file */
10505 if (child_kind != svn_node_file)
10507 svn_hash_sets(*iprop_paths, child_abspath, NULL);
10511 svn_pool_destroy(iterpool);
10514 return SVN_NO_ERROR;
10518 svn_wc__db_get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10520 const char *local_abspath,
10522 apr_pool_t *result_pool,
10523 apr_pool_t *scratch_pool)
10525 svn_wc__db_wcroot_t *wcroot;
10526 const char *local_relpath;
10528 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10530 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10531 local_abspath, scratch_pool,
10533 VERIFY_USABLE_WCROOT(wcroot);
10535 SVN_WC__DB_WITH_TXN(
10536 get_children_with_cached_iprops(iprop_paths, wcroot, local_relpath,
10537 depth, result_pool, scratch_pool),
10540 return SVN_NO_ERROR;
10544 svn_wc__db_read_children_of_working_node(const apr_array_header_t **children,
10546 const char *local_abspath,
10547 apr_pool_t *result_pool,
10548 apr_pool_t *scratch_pool)
10550 svn_wc__db_wcroot_t *wcroot;
10551 const char *local_relpath;
10553 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10555 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10557 scratch_pool, scratch_pool));
10558 VERIFY_USABLE_WCROOT(wcroot);
10560 return gather_children2(children, wcroot, local_relpath,
10561 result_pool, scratch_pool);
10564 /* Helper for svn_wc__db_node_check_replace().
10566 static svn_error_t *
10567 check_replace_txn(svn_boolean_t *is_replace_root_p,
10568 svn_boolean_t *base_replace_p,
10569 svn_boolean_t *is_replace_p,
10570 svn_wc__db_wcroot_t *wcroot,
10571 const char *local_relpath,
10572 apr_pool_t *scratch_pool)
10574 svn_sqlite__stmt_t *stmt;
10575 svn_boolean_t have_row;
10576 svn_boolean_t is_replace = FALSE;
10577 int replaced_op_depth;
10578 svn_wc__db_status_t replaced_status;
10580 /* Our caller initialized the output values to FALSE */
10582 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10583 STMT_SELECT_NODE_INFO));
10585 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10587 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10590 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10591 svn_sqlite__reset(stmt),
10592 _("The node '%s' was not found."),
10593 path_for_error_message(wcroot, local_relpath,
10597 svn_wc__db_status_t status;
10599 status = svn_sqlite__column_token(stmt, 3, presence_map);
10601 if (status != svn_wc__db_status_normal)
10602 return svn_error_trace(svn_sqlite__reset(stmt));
10605 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10608 return svn_error_trace(svn_sqlite__reset(stmt));
10610 replaced_status = svn_sqlite__column_token(stmt, 3, presence_map);
10612 /* If the layer below the add describes a not present or a deleted node,
10613 this is not a replacement. Deleted can only occur if an ancestor is
10614 the delete root. */
10615 if (replaced_status != svn_wc__db_status_not_present
10616 && replaced_status != svn_wc__db_status_excluded
10617 && replaced_status != svn_wc__db_status_server_excluded
10618 && replaced_status != svn_wc__db_status_base_deleted)
10622 *is_replace_p = TRUE;
10625 replaced_op_depth = svn_sqlite__column_int(stmt, 0);
10627 if (base_replace_p)
10629 int op_depth = svn_sqlite__column_int(stmt, 0);
10631 while (op_depth != 0 && have_row)
10633 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10636 op_depth = svn_sqlite__column_int(stmt, 0);
10639 if (have_row && op_depth == 0)
10641 svn_wc__db_status_t base_status;
10643 base_status = svn_sqlite__column_token(stmt, 3, presence_map);
10645 *base_replace_p = (base_status != svn_wc__db_status_not_present);
10649 SVN_ERR(svn_sqlite__reset(stmt));
10651 if (!is_replace_root_p || !is_replace)
10652 return SVN_NO_ERROR;
10654 if (replaced_status != svn_wc__db_status_base_deleted)
10656 int parent_op_depth;
10658 /* Check the current op-depth of the parent to see if we are a replacement
10660 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
10661 svn_relpath_dirname(local_relpath,
10664 SVN_ERR(svn_sqlite__step_row(stmt)); /* Parent must exist as 'normal' */
10666 parent_op_depth = svn_sqlite__column_int(stmt, 0);
10668 if (parent_op_depth >= replaced_op_depth)
10670 /* Did we replace inside our directory? */
10672 *is_replace_root_p = (parent_op_depth == replaced_op_depth);
10673 SVN_ERR(svn_sqlite__reset(stmt));
10674 return SVN_NO_ERROR;
10677 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10680 parent_op_depth = svn_sqlite__column_int(stmt, 0);
10682 SVN_ERR(svn_sqlite__reset(stmt));
10685 *is_replace_root_p = TRUE; /* Parent is no replacement */
10686 else if (parent_op_depth < replaced_op_depth)
10687 *is_replace_root_p = TRUE; /* Parent replaces a lower layer */
10688 /*else // No replacement root */
10691 return SVN_NO_ERROR;
10695 svn_wc__db_node_check_replace(svn_boolean_t *is_replace_root,
10696 svn_boolean_t *base_replace,
10697 svn_boolean_t *is_replace,
10699 const char *local_abspath,
10700 apr_pool_t *scratch_pool)
10702 svn_wc__db_wcroot_t *wcroot;
10703 const char *local_relpath;
10705 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10707 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10709 scratch_pool, scratch_pool));
10710 VERIFY_USABLE_WCROOT(wcroot);
10712 if (is_replace_root)
10713 *is_replace_root = FALSE;
10715 *base_replace = FALSE;
10717 *is_replace = FALSE;
10719 if (local_relpath[0] == '\0')
10720 return SVN_NO_ERROR; /* Working copy root can't be replaced */
10722 SVN_WC__DB_WITH_TXN(
10723 check_replace_txn(is_replace_root, base_replace, is_replace,
10724 wcroot, local_relpath, scratch_pool),
10727 return SVN_NO_ERROR;
10731 svn_wc__db_read_children(const apr_array_header_t **children,
10733 const char *local_abspath,
10734 apr_pool_t *result_pool,
10735 apr_pool_t *scratch_pool)
10737 svn_wc__db_wcroot_t *wcroot;
10738 const char *local_relpath;
10740 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10742 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10744 scratch_pool, scratch_pool));
10745 VERIFY_USABLE_WCROOT(wcroot);
10747 return gather_children(children, wcroot, local_relpath,
10748 result_pool, scratch_pool);
10753 static svn_error_t *
10754 relocate_txn(svn_wc__db_wcroot_t *wcroot,
10755 const char *local_relpath,
10756 const char *repos_root_url,
10757 const char *repos_uuid,
10758 svn_boolean_t have_base_node,
10759 apr_int64_t old_repos_id,
10760 apr_pool_t *scratch_pool)
10762 svn_sqlite__stmt_t *stmt;
10763 apr_int64_t new_repos_id;
10765 /* This function affects all the children of the given local_relpath,
10766 but the way that it does this is through the repos inheritance mechanism.
10767 So, we only need to rewrite the repos_id of the given local_relpath,
10768 as well as any children with a non-null repos_id, as well as various
10769 repos_id fields in the locks and working_node tables.
10772 /* Get the repos_id for the new repository. */
10773 SVN_ERR(create_repos_id(&new_repos_id, repos_root_url, repos_uuid,
10774 wcroot->sdb, scratch_pool));
10776 /* Set the (base and working) repos_ids and clear the dav_caches */
10777 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10778 STMT_RECURSIVE_UPDATE_NODE_REPO));
10779 SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
10780 old_repos_id, new_repos_id));
10781 SVN_ERR(svn_sqlite__step_done(stmt));
10783 if (have_base_node)
10785 /* Update any locks for the root or its children. */
10786 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10787 STMT_UPDATE_LOCK_REPOS_ID));
10788 SVN_ERR(svn_sqlite__bindf(stmt, "ii", old_repos_id, new_repos_id));
10789 SVN_ERR(svn_sqlite__step_done(stmt));
10792 return SVN_NO_ERROR;
10797 svn_wc__db_global_relocate(svn_wc__db_t *db,
10798 const char *local_dir_abspath,
10799 const char *repos_root_url,
10800 apr_pool_t *scratch_pool)
10802 svn_wc__db_wcroot_t *wcroot;
10803 const char *local_relpath;
10804 const char *local_dir_relpath;
10805 svn_wc__db_status_t status;
10806 const char *repos_uuid;
10807 svn_boolean_t have_base_node;
10808 apr_int64_t old_repos_id;
10810 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
10811 /* ### assert that we were passed a directory? */
10813 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_dir_relpath,
10814 db, local_dir_abspath, scratch_pool, scratch_pool));
10815 VERIFY_USABLE_WCROOT(wcroot);
10816 local_relpath = local_dir_relpath;
10818 SVN_ERR(read_info(&status,
10819 NULL, NULL, NULL, &old_repos_id,
10820 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10821 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10823 &have_base_node, NULL, NULL,
10824 wcroot, local_relpath,
10825 scratch_pool, scratch_pool));
10827 if (status == svn_wc__db_status_excluded)
10829 /* The parent cannot be excluded, so look at the parent and then
10830 adjust the relpath */
10831 const char *parent_relpath = svn_relpath_dirname(local_dir_relpath,
10833 SVN_ERR(read_info(&status,
10834 NULL, NULL, NULL, &old_repos_id,
10835 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10836 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10839 wcroot, parent_relpath,
10840 scratch_pool, scratch_pool));
10841 local_dir_relpath = parent_relpath;
10844 if (old_repos_id == INVALID_REPOS_ID)
10846 /* Do we need to support relocating something that is
10847 added/deleted/excluded without relocating the parent? If not
10848 then perhaps relpath, root_url and uuid should be passed down
10849 to the children so that they don't have to scan? */
10851 if (status == svn_wc__db_status_deleted)
10853 const char *work_del_relpath;
10855 SVN_ERR(scan_deletion_txn(NULL, NULL,
10856 &work_del_relpath, NULL,
10857 wcroot, local_dir_relpath,
10860 if (work_del_relpath)
10862 /* Deleted within a copy/move */
10864 /* The parent of the delete is added. */
10865 status = svn_wc__db_status_added;
10866 local_dir_relpath = svn_relpath_dirname(work_del_relpath,
10871 if (status == svn_wc__db_status_added)
10873 SVN_ERR(scan_addition(NULL, NULL, NULL, &old_repos_id,
10874 NULL, NULL, NULL, NULL, NULL, NULL,
10875 wcroot, local_dir_relpath,
10876 scratch_pool, scratch_pool));
10879 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL,
10881 NULL, NULL, NULL, NULL, NULL,
10882 NULL, NULL, NULL, NULL, NULL,
10883 wcroot, local_dir_relpath,
10884 scratch_pool, scratch_pool));
10887 SVN_ERR(svn_wc__db_fetch_repos_info(NULL, &repos_uuid, wcroot->sdb,
10888 old_repos_id, scratch_pool));
10889 SVN_ERR_ASSERT(repos_uuid);
10891 SVN_WC__DB_WITH_TXN(
10892 relocate_txn(wcroot, local_relpath, repos_root_url, repos_uuid,
10893 have_base_node, old_repos_id, scratch_pool),
10896 return SVN_NO_ERROR;
10900 /* Helper for commit_node()
10901 Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of
10902 (WCROOT, LOCAL_RELPATH), directly if its BASE row exists or implied from
10903 its parent's BASE row if not. In the latter case, error if the parent
10904 BASE row does not exist. */
10905 static svn_error_t *
10906 determine_commit_repos_info(apr_int64_t *repos_id,
10907 const char **repos_relpath,
10908 svn_wc__db_wcroot_t *wcroot,
10909 const char *local_relpath,
10910 apr_pool_t *result_pool,
10911 apr_pool_t *scratch_pool)
10913 svn_sqlite__stmt_t *stmt;
10914 svn_boolean_t have_row;
10917 /* Prefer the current node's repository information. */
10918 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10919 STMT_SELECT_NODE_INFO));
10920 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10921 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10924 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10925 svn_sqlite__reset(stmt),
10926 _("The node '%s' was not found."),
10927 path_for_error_message(wcroot, local_relpath,
10930 op_depth = svn_sqlite__column_int(stmt, 0);
10934 svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 3,
10937 if (presence == svn_wc__db_status_base_deleted)
10939 SVN_ERR(svn_sqlite__step_row(stmt)); /* There must be a row */
10940 op_depth = svn_sqlite__column_int(stmt, 0);
10944 const char *parent_repos_relpath;
10945 const char *parent_relpath;
10948 SVN_ERR(svn_sqlite__reset(stmt));
10950 /* The repository relative path of an add/copy is based on its
10951 ancestor, not on the shadowed base layer.
10953 As this function is only used from the commit processing we know
10954 the parent directory has only a BASE row, so we can just obtain
10955 the information directly by recursing (once!) */
10957 svn_relpath_split(&parent_relpath, &name, local_relpath,
10960 SVN_ERR(determine_commit_repos_info(repos_id, &parent_repos_relpath,
10961 wcroot, parent_relpath,
10962 scratch_pool, scratch_pool));
10964 *repos_relpath = svn_relpath_join(parent_repos_relpath, name,
10966 return SVN_NO_ERROR;
10971 SVN_ERR_ASSERT(op_depth == 0); /* And that row must be BASE */
10973 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1));
10974 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 2));
10976 *repos_id = svn_sqlite__column_int64(stmt, 1);
10977 *repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
10979 return svn_error_trace(svn_sqlite__reset(stmt));
10982 /* Helper for svn_wc__db_global_commit()
10984 Makes local_relpath and all its descendants at the same op-depth represent
10985 the copy origin repos_id:repos_relpath@revision.
10987 This code is only valid to fix-up a move from an old location, to a new
10988 location during a commit.
10991 * local_relpath is not the working copy root (can't be moved)
10992 * repos_relpath is not the repository root (can't be moved)
10994 static svn_error_t *
10995 moved_descendant_commit(svn_wc__db_wcroot_t *wcroot,
10996 const char *local_relpath,
10998 apr_int64_t repos_id,
10999 const char *repos_relpath,
11000 svn_revnum_t revision,
11001 apr_pool_t *scratch_pool)
11003 apr_hash_t *children;
11004 apr_pool_t *iterpool;
11005 svn_sqlite__stmt_t *stmt;
11006 svn_boolean_t have_row;
11007 apr_hash_index_t *hi;
11009 SVN_ERR_ASSERT(*local_relpath != '\0'
11010 && *repos_relpath != '\0');
11012 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11013 STMT_SELECT_MOVED_DESCENDANTS));
11014 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
11018 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11020 return svn_error_trace(svn_sqlite__reset(stmt));
11022 children = apr_hash_make(scratch_pool);
11024 /* First, obtain all moved children */
11025 /* To keep error handling simple, first cache them in a hashtable */
11028 const char *src_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
11029 const char *to_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
11031 svn_hash_sets(children, src_relpath, to_relpath);
11033 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11035 SVN_ERR(svn_sqlite__reset(stmt));
11037 /* Then update them */
11038 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11039 STMT_COMMIT_UPDATE_ORIGIN));
11041 iterpool = svn_pool_create(scratch_pool);
11042 for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
11044 const char *src_relpath = svn__apr_hash_index_key(hi);
11045 const char *to_relpath = svn__apr_hash_index_val(hi);
11046 const char *new_repos_relpath;
11047 int to_op_depth = relpath_depth(to_relpath);
11050 svn_pool_clear(iterpool);
11052 SVN_ERR_ASSERT(to_op_depth > 0);
11054 new_repos_relpath = svn_relpath_join(
11056 svn_relpath_skip_ancestor(local_relpath,
11060 SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11066 SVN_ERR(svn_sqlite__update(&affected, stmt));
11069 /* Enable in release code?
11070 Broken moves are not fatal yet, but this assertion would break
11072 SVN_ERR_ASSERT(affected >= 1); /* If this fails there is no move dest */
11075 SVN_ERR(moved_descendant_commit(wcroot, to_relpath, to_op_depth,
11076 repos_id, new_repos_relpath, revision,
11080 svn_pool_destroy(iterpool);
11081 return SVN_NO_ERROR;
11084 /* Helper for svn_wc__db_global_commit()
11086 Moves all nodes below LOCAL_RELPATH from op-depth OP_DEPTH to op-depth 0
11087 (BASE), setting their presence to 'not-present' if their presence wasn't
11090 Makes all nodes below LOCAL_RELPATH represent the descendants of repository
11091 location repos_id:repos_relpath@revision.
11094 * local_relpath is not the working copy root (can't be replaced)
11095 * repos_relpath is not the repository root (can't be replaced)
11097 static svn_error_t *
11098 descendant_commit(svn_wc__db_wcroot_t *wcroot,
11099 const char *local_relpath,
11101 apr_int64_t repos_id,
11102 const char *repos_relpath,
11103 svn_revnum_t revision,
11104 apr_pool_t *scratch_pool)
11106 svn_sqlite__stmt_t *stmt;
11108 SVN_ERR_ASSERT(*local_relpath != '\0'
11109 && *repos_relpath != '\0');
11111 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11112 STMT_COMMIT_DESCENDANTS_TO_BASE));
11114 SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11121 SVN_ERR(svn_sqlite__update(NULL, stmt));
11123 return SVN_NO_ERROR;
11126 /* The body of svn_wc__db_global_commit().
11128 static svn_error_t *
11129 commit_node(svn_wc__db_wcroot_t *wcroot,
11130 const char *local_relpath,
11131 svn_revnum_t new_revision,
11132 svn_revnum_t changed_rev,
11133 apr_time_t changed_date,
11134 const char *changed_author,
11135 const svn_checksum_t *new_checksum,
11136 const apr_array_header_t *new_children,
11137 apr_hash_t *new_dav_cache,
11138 svn_boolean_t keep_changelist,
11139 svn_boolean_t no_unlock,
11140 const svn_skel_t *work_items,
11141 apr_pool_t *scratch_pool)
11143 svn_sqlite__stmt_t *stmt_info;
11144 svn_sqlite__stmt_t *stmt_act;
11145 svn_boolean_t have_act;
11146 svn_string_t prop_blob = { 0 };
11147 svn_string_t inherited_prop_blob = { 0 };
11148 const char *changelist = NULL;
11149 const char *parent_relpath;
11150 svn_wc__db_status_t new_presence;
11151 svn_node_kind_t new_kind;
11152 const char *new_depth_str = NULL;
11153 svn_sqlite__stmt_t *stmt;
11154 apr_int64_t repos_id;
11155 const char *repos_relpath;
11157 svn_wc__db_status_t old_presence;
11159 /* If we are adding a file or directory, then we need to get
11160 repository information from the parent node since "this node" does
11163 For existing nodes, we should retain the (potentially-switched)
11164 repository information. */
11165 SVN_ERR(determine_commit_repos_info(&repos_id, &repos_relpath,
11166 wcroot, local_relpath,
11167 scratch_pool, scratch_pool));
11169 /* ### is it better to select only the data needed? */
11170 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
11171 STMT_SELECT_NODE_INFO));
11172 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
11173 SVN_ERR(svn_sqlite__step_row(stmt_info));
11175 SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
11176 STMT_SELECT_ACTUAL_NODE));
11177 SVN_ERR(svn_sqlite__bindf(stmt_act, "is",
11178 wcroot->wc_id, local_relpath));
11179 SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
11181 /* There should be something to commit! */
11183 op_depth = svn_sqlite__column_int(stmt_info, 0);
11185 /* Figure out the new node's kind. It will be whatever is in WORKING_NODE,
11186 or there will be a BASE_NODE that has it. */
11187 new_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
11189 /* What will the new depth be? */
11190 if (new_kind == svn_node_dir)
11191 new_depth_str = svn_sqlite__column_text(stmt_info, 11, scratch_pool);
11193 /* Check that the repository information is not being changed. */
11196 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 1));
11197 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 2));
11199 /* A commit cannot change these values. */
11200 SVN_ERR_ASSERT(repos_id == svn_sqlite__column_int64(stmt_info, 1));
11201 SVN_ERR_ASSERT(strcmp(repos_relpath,
11202 svn_sqlite__column_text(stmt_info, 2, NULL)) == 0);
11205 /* Find the appropriate new properties -- ACTUAL overrides any properties
11206 in WORKING that arrived as part of a copy/move.
11208 Note: we'll keep them as a big blob of data, rather than
11209 deserialize/serialize them. */
11211 prop_blob.data = svn_sqlite__column_blob(stmt_act, 1, &prop_blob.len,
11213 if (prop_blob.data == NULL)
11214 prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len,
11217 inherited_prop_blob.data = svn_sqlite__column_blob(stmt_info, 16,
11218 &inherited_prop_blob.len,
11221 if (keep_changelist && have_act)
11222 changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool);
11224 old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map);
11226 /* ### other stuff? */
11228 SVN_ERR(svn_sqlite__reset(stmt_info));
11229 SVN_ERR(svn_sqlite__reset(stmt_act));
11235 /* This removes all layers of this node and at the same time determines
11236 if we need to remove shadowed layers below our descendants. */
11238 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11239 STMT_DELETE_NODE_ALL_LAYERS));
11240 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11241 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
11243 if (affected_rows > 1)
11245 /* We commit a shadowing operation
11247 1) Remove all shadowed nodes
11248 2) And remove all nodes that have a base-deleted as lowest layer,
11249 because 1) removed that layer */
11251 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11252 STMT_DELETE_SHADOWED_RECURSIVE));
11254 SVN_ERR(svn_sqlite__bindf(stmt,
11260 SVN_ERR(svn_sqlite__step_done(stmt));
11263 /* Note that while these two calls look so similar that they might
11264 be integrated, they really affect a different op-depth and
11265 completely different nodes (via a different recursion pattern). */
11267 /* Collapse descendants of the current op_depth in layer 0 */
11268 SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth,
11269 repos_id, repos_relpath, new_revision,
11272 /* And make the recorded local moves represent moves of the node we just
11274 SVN_ERR(moved_descendant_commit(wcroot, local_relpath, 0,
11275 repos_id, repos_relpath, new_revision,
11278 /* This node is no longer modified, so no node was moved here */
11279 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11280 STMT_CLEAR_MOVED_TO_FROM_DEST));
11281 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
11284 SVN_ERR(svn_sqlite__step_done(stmt));
11287 /* Update or add the BASE_NODE row with all the new information. */
11289 if (*local_relpath == '\0')
11290 parent_relpath = NULL;
11292 parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
11294 /* Preserve any incomplete status */
11295 new_presence = (old_presence == svn_wc__db_status_incomplete
11296 ? svn_wc__db_status_incomplete
11297 : svn_wc__db_status_normal);
11299 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11300 STMT_APPLY_CHANGES_TO_BASE_NODE));
11301 /* symlink_target not yet used */
11302 SVN_ERR(svn_sqlite__bindf(stmt, "issisrtstrisnbn",
11303 wcroot->wc_id, local_relpath,
11308 presence_map, new_presence,
11310 kind_map, new_kind,
11314 prop_blob.data, prop_blob.len));
11316 SVN_ERR(svn_sqlite__bind_checksum(stmt, 13, new_checksum,
11318 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, new_dav_cache,
11320 if (inherited_prop_blob.data != NULL)
11322 SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data,
11323 inherited_prop_blob.len));
11326 SVN_ERR(svn_sqlite__step_done(stmt));
11330 if (keep_changelist && changelist != NULL)
11332 /* The user told us to keep the changelist. Replace the row in
11333 ACTUAL_NODE with the basic keys and the changelist. */
11334 SVN_ERR(svn_sqlite__get_statement(
11335 &stmt, wcroot->sdb,
11336 STMT_RESET_ACTUAL_WITH_CHANGELIST));
11337 SVN_ERR(svn_sqlite__bindf(stmt, "isss",
11338 wcroot->wc_id, local_relpath,
11339 svn_relpath_dirname(local_relpath,
11342 SVN_ERR(svn_sqlite__step_done(stmt));
11346 /* Toss the ACTUAL_NODE row. */
11347 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11348 STMT_DELETE_ACTUAL_NODE));
11349 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11350 SVN_ERR(svn_sqlite__step_done(stmt));
11354 if (new_kind == svn_node_dir)
11356 /* When committing a directory, we should have its new children. */
11357 /* ### one day. just not today. */
11359 SVN_ERR_ASSERT(new_children != NULL);
11362 /* ### process the children */
11367 svn_sqlite__stmt_t *lock_stmt;
11369 SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
11370 STMT_DELETE_LOCK_RECURSIVELY));
11371 SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
11372 SVN_ERR(svn_sqlite__step_done(lock_stmt));
11375 /* Install any work items into the queue, as part of this transaction. */
11376 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
11378 return SVN_NO_ERROR;
11383 svn_wc__db_global_commit(svn_wc__db_t *db,
11384 const char *local_abspath,
11385 svn_revnum_t new_revision,
11386 svn_revnum_t changed_revision,
11387 apr_time_t changed_date,
11388 const char *changed_author,
11389 const svn_checksum_t *new_checksum,
11390 const apr_array_header_t *new_children,
11391 apr_hash_t *new_dav_cache,
11392 svn_boolean_t keep_changelist,
11393 svn_boolean_t no_unlock,
11394 const svn_skel_t *work_items,
11395 apr_pool_t *scratch_pool)
11397 const char *local_relpath;
11398 svn_wc__db_wcroot_t *wcroot;
11400 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11401 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
11402 SVN_ERR_ASSERT(new_checksum == NULL || new_children == NULL);
11404 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11405 local_abspath, scratch_pool, scratch_pool));
11406 VERIFY_USABLE_WCROOT(wcroot);
11408 SVN_WC__DB_WITH_TXN(
11409 commit_node(wcroot, local_relpath,
11410 new_revision, changed_revision, changed_date, changed_author,
11411 new_checksum, new_children, new_dav_cache, keep_changelist,
11412 no_unlock, work_items, scratch_pool),
11415 /* We *totally* monkeyed the entries. Toss 'em. */
11416 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
11418 return SVN_NO_ERROR;
11423 svn_wc__db_global_update(svn_wc__db_t *db,
11424 const char *local_abspath,
11425 svn_node_kind_t new_kind,
11426 const char *new_repos_relpath,
11427 svn_revnum_t new_revision,
11428 const apr_hash_t *new_props,
11429 svn_revnum_t new_changed_rev,
11430 apr_time_t new_changed_date,
11431 const char *new_changed_author,
11432 const apr_array_header_t *new_children,
11433 const svn_checksum_t *new_checksum,
11434 const char *new_target,
11435 const apr_hash_t *new_dav_cache,
11436 const svn_skel_t *conflict,
11437 const svn_skel_t *work_items,
11438 apr_pool_t *scratch_pool)
11443 svn_wc__db_wcroot_t *wcroot;
11444 const char *local_relpath;
11446 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11447 /* ### allow NULL for NEW_REPOS_RELPATH to indicate "no change"? */
11448 SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
11449 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
11450 SVN_ERR_ASSERT(new_props != NULL);
11451 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_changed_rev));
11452 SVN_ERR_ASSERT((new_children != NULL
11453 && new_checksum == NULL
11454 && new_target == NULL)
11455 || (new_children == NULL
11456 && new_checksum != NULL
11457 && new_target == NULL)
11458 || (new_children == NULL
11459 && new_checksum == NULL
11460 && new_target != NULL));
11462 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11463 local_abspath, scratch_pool, scratch_pool));
11464 VERIFY_USABLE_WCROOT(wcroot);
11466 SVN_WC__DB_WITH_TXN(
11467 update_node(wcroot, local_relpath,
11468 new_repos_relpath, new_revision, new_props,
11469 new_changed_rev, new_changed_date, new_changed_author,
11470 new_children, new_checksum, new_target,
11471 conflict, work_items, scratch_pool),
11474 /* We *totally* monkeyed the entries. Toss 'em. */
11475 SVN_ERR(flush_entries(wcroot, local_abspath, scratch_pool));
11477 return SVN_NO_ERROR;
11481 /* Sets a base nodes revision, repository relative path, and/or inherited
11482 propertis. If LOCAL_ABSPATH's rev (REV) is valid, set its revision. If
11483 SET_REPOS_RELPATH is TRUE set its repository relative path to REPOS_RELPATH
11484 (and make sure its REPOS_ID is still valid). If IPROPS is not NULL set its
11485 inherited properties to IPROPS, if IPROPS is NULL then clear any the iprops
11486 cache for the base node.
11488 static svn_error_t *
11489 db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot,
11490 const char *local_relpath,
11491 apr_array_header_t *iprops,
11493 svn_boolean_t set_repos_relpath,
11494 const char *repos_relpath,
11495 apr_int64_t repos_id,
11496 apr_pool_t *scratch_pool)
11498 svn_sqlite__stmt_t *stmt;
11500 SVN_ERR(flush_entries(wcroot,
11501 svn_dirent_join(wcroot->abspath, local_relpath,
11503 svn_depth_empty, scratch_pool));
11506 if (SVN_IS_VALID_REVNUM(rev))
11508 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11509 STMT_UPDATE_BASE_REVISION));
11511 SVN_ERR(svn_sqlite__bindf(stmt, "isr", wcroot->wc_id, local_relpath,
11514 SVN_ERR(svn_sqlite__step_done(stmt));
11517 if (set_repos_relpath)
11519 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11520 STMT_UPDATE_BASE_REPOS));
11522 SVN_ERR(svn_sqlite__bindf(stmt, "isis", wcroot->wc_id, local_relpath,
11523 repos_id, repos_relpath));
11525 SVN_ERR(svn_sqlite__step_done(stmt));
11528 /* Set or clear iprops. */
11529 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11530 STMT_UPDATE_IPROP));
11531 SVN_ERR(svn_sqlite__bindf(stmt, "is",
11534 SVN_ERR(svn_sqlite__bind_iprops(stmt, 3, iprops, scratch_pool));
11535 SVN_ERR(svn_sqlite__step_done(stmt));
11537 return SVN_NO_ERROR;
11540 /* The main body of bump_revisions_post_update().
11542 * Tweak the information for LOCAL_RELPATH in WCROOT. If NEW_REPOS_RELPATH is
11543 * non-NULL update the entry to the new url specified by NEW_REPOS_RELPATH,
11544 * NEW_REPOS_ID. If NEW_REV is valid, make this the node's working revision.
11546 * If WCROOT_IPROPS is not NULL it is a hash mapping const char * absolute
11547 * working copy paths to depth-first ordered arrays of
11548 * svn_prop_inherited_item_t * structures. If the absolute path equivalent
11549 * of LOCAL_RELPATH exists in WCROOT_IPROPS, then set the hashed value as the
11550 * node's inherited properties.
11552 * Unless S_ROOT is TRUE the tweaks might cause the node for LOCAL_ABSPATH to
11553 * be removed from the WC; if IS_ROOT is TRUE this will not happen.
11555 static svn_error_t *
11556 bump_node_revision(svn_wc__db_wcroot_t *wcroot,
11557 const char *local_relpath,
11558 apr_int64_t new_repos_id,
11559 const char *new_repos_relpath,
11560 svn_revnum_t new_rev,
11562 apr_hash_t *exclude_relpaths,
11563 apr_hash_t *wcroot_iprops,
11564 svn_boolean_t is_root,
11565 svn_boolean_t skip_when_dir,
11567 apr_pool_t *scratch_pool)
11569 apr_pool_t *iterpool;
11570 const apr_array_header_t *children;
11572 svn_wc__db_status_t status;
11573 svn_node_kind_t db_kind;
11574 svn_revnum_t revision;
11575 const char *repos_relpath;
11576 apr_int64_t repos_id;
11577 svn_boolean_t set_repos_relpath = FALSE;
11578 svn_boolean_t update_root;
11579 svn_depth_t depth_below_here = depth;
11580 apr_array_header_t *iprops = NULL;
11582 /* Skip an excluded path and its descendants. */
11583 if (svn_hash_gets(exclude_relpaths, local_relpath))
11584 return SVN_NO_ERROR;
11586 SVN_ERR(svn_wc__db_base_get_info_internal(&status, &db_kind, &revision,
11587 &repos_relpath, &repos_id,
11588 NULL, NULL, NULL, NULL, NULL,
11589 NULL, NULL, NULL, NULL, &update_root,
11590 wcroot, local_relpath,
11591 scratch_pool, scratch_pool));
11593 /* Skip file externals */
11595 && db_kind == svn_node_file
11597 return SVN_NO_ERROR;
11599 if (skip_when_dir && db_kind == svn_node_dir)
11600 return SVN_NO_ERROR;
11602 /* If the node is still marked 'not-present', then the server did not
11603 re-add it. So it's really gone in this revision, thus we remove the node.
11605 If the node is still marked 'server-excluded' and yet is not the same
11606 revision as new_rev, then the server did not re-add it, nor
11607 re-server-exclude it, so we can remove the node. */
11609 && (status == svn_wc__db_status_not_present
11610 || (status == svn_wc__db_status_server_excluded &&
11611 revision != new_rev)))
11613 return svn_error_trace(db_base_remove(wcroot, local_relpath,
11614 db, FALSE, FALSE, FALSE,
11615 SVN_INVALID_REVNUM,
11616 NULL, NULL, scratch_pool));
11619 if (new_repos_relpath != NULL && strcmp(repos_relpath, new_repos_relpath))
11620 set_repos_relpath = TRUE;
11623 iprops = svn_hash_gets(wcroot_iprops,
11624 svn_dirent_join(wcroot->abspath, local_relpath,
11628 || set_repos_relpath
11629 || (SVN_IS_VALID_REVNUM(new_rev) && new_rev != revision))
11631 SVN_ERR(db_op_set_rev_repos_relpath_iprops(wcroot, local_relpath,
11640 if (depth <= svn_depth_empty
11641 || db_kind != svn_node_dir
11642 || status == svn_wc__db_status_server_excluded
11643 || status == svn_wc__db_status_excluded
11644 || status == svn_wc__db_status_not_present)
11645 return SVN_NO_ERROR;
11647 /* And now recurse over the children */
11649 depth_below_here = depth;
11651 if (depth == svn_depth_immediates || depth == svn_depth_files)
11652 depth_below_here = svn_depth_empty;
11654 iterpool = svn_pool_create(scratch_pool);
11656 SVN_ERR(gather_repo_children(&children, wcroot, local_relpath, 0,
11657 scratch_pool, iterpool));
11658 for (i = 0; i < children->nelts; i++)
11660 const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
11661 const char *child_local_relpath;
11662 const char *child_repos_relpath = NULL;
11664 svn_pool_clear(iterpool);
11666 /* Derive the new URL for the current (child) entry */
11667 if (new_repos_relpath)
11668 child_repos_relpath = svn_relpath_join(new_repos_relpath,
11669 child_basename, iterpool);
11671 child_local_relpath = svn_relpath_join(local_relpath, child_basename,
11674 SVN_ERR(bump_node_revision(wcroot, child_local_relpath, new_repos_id,
11675 child_repos_relpath, new_rev,
11677 exclude_relpaths, wcroot_iprops,
11678 FALSE /* is_root */,
11679 (depth < svn_depth_immediates), db,
11684 svn_pool_destroy(iterpool);
11686 return SVN_NO_ERROR;
11689 /* Helper for svn_wc__db_op_bump_revisions_post_update().
11691 static svn_error_t *
11692 bump_revisions_post_update(svn_wc__db_wcroot_t *wcroot,
11693 const char *local_relpath,
11696 const char *new_repos_relpath,
11697 const char *new_repos_root_url,
11698 const char *new_repos_uuid,
11699 svn_revnum_t new_revision,
11700 apr_hash_t *exclude_relpaths,
11701 apr_hash_t *wcroot_iprops,
11702 svn_wc_notify_func2_t notify_func,
11703 void *notify_baton,
11704 apr_pool_t *scratch_pool)
11706 svn_wc__db_status_t status;
11707 svn_node_kind_t kind;
11709 apr_int64_t new_repos_id = INVALID_REPOS_ID;
11711 err = svn_wc__db_base_get_info_internal(&status, &kind, NULL, NULL, NULL,
11712 NULL, NULL, NULL, NULL, NULL, NULL,
11713 NULL, NULL, NULL, NULL,
11714 wcroot, local_relpath,
11715 scratch_pool, scratch_pool);
11716 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
11718 svn_error_clear(err);
11719 return SVN_NO_ERROR;
11726 case svn_wc__db_status_excluded:
11727 case svn_wc__db_status_server_excluded:
11728 case svn_wc__db_status_not_present:
11729 return SVN_NO_ERROR;
11731 /* Explicitly ignore other statii */
11736 if (new_repos_root_url != NULL)
11737 SVN_ERR(create_repos_id(&new_repos_id, new_repos_root_url,
11739 wcroot->sdb, scratch_pool));
11741 SVN_ERR(bump_node_revision(wcroot, local_relpath, new_repos_id,
11742 new_repos_relpath, new_revision,
11743 depth, exclude_relpaths,
11745 TRUE /* is_root */, FALSE, db,
11748 SVN_ERR(svn_wc__db_bump_moved_away(wcroot, local_relpath, depth, db,
11751 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, SVN_INVALID_REVNUM,
11752 SVN_INVALID_REVNUM, notify_func,
11753 notify_baton, scratch_pool));
11755 return SVN_NO_ERROR;
11759 svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db,
11760 const char *local_abspath,
11762 const char *new_repos_relpath,
11763 const char *new_repos_root_url,
11764 const char *new_repos_uuid,
11765 svn_revnum_t new_revision,
11766 apr_hash_t *exclude_relpaths,
11767 apr_hash_t *wcroot_iprops,
11768 svn_wc_notify_func2_t notify_func,
11769 void *notify_baton,
11770 apr_pool_t *scratch_pool)
11772 const char *local_relpath;
11773 svn_wc__db_wcroot_t *wcroot;
11775 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11776 local_abspath, scratch_pool, scratch_pool));
11778 VERIFY_USABLE_WCROOT(wcroot);
11780 if (svn_hash_gets(exclude_relpaths, local_relpath))
11781 return SVN_NO_ERROR;
11783 if (depth == svn_depth_unknown)
11784 depth = svn_depth_infinity;
11786 SVN_WC__DB_WITH_TXN(
11787 bump_revisions_post_update(wcroot, local_relpath, db,
11788 depth, new_repos_relpath, new_repos_root_url,
11789 new_repos_uuid, new_revision,
11790 exclude_relpaths, wcroot_iprops,
11791 notify_func, notify_baton, scratch_pool),
11794 return SVN_NO_ERROR;
11797 /* The body of svn_wc__db_lock_add().
11799 static svn_error_t *
11800 lock_add_txn(svn_wc__db_wcroot_t *wcroot,
11801 const char *local_relpath,
11802 const svn_wc__db_lock_t *lock,
11803 apr_pool_t *scratch_pool)
11805 svn_sqlite__stmt_t *stmt;
11806 const char *repos_relpath;
11807 apr_int64_t repos_id;
11809 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11810 &repos_relpath, &repos_id,
11811 NULL, NULL, NULL, NULL, NULL,
11812 NULL, NULL, NULL, NULL, NULL,
11813 wcroot, local_relpath,
11814 scratch_pool, scratch_pool));
11816 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_LOCK));
11817 SVN_ERR(svn_sqlite__bindf(stmt, "iss",
11818 repos_id, repos_relpath, lock->token));
11820 if (lock->owner != NULL)
11821 SVN_ERR(svn_sqlite__bind_text(stmt, 4, lock->owner));
11823 if (lock->comment != NULL)
11824 SVN_ERR(svn_sqlite__bind_text(stmt, 5, lock->comment));
11826 if (lock->date != 0)
11827 SVN_ERR(svn_sqlite__bind_int64(stmt, 6, lock->date));
11829 SVN_ERR(svn_sqlite__insert(NULL, stmt));
11831 return SVN_NO_ERROR;
11836 svn_wc__db_lock_add(svn_wc__db_t *db,
11837 const char *local_abspath,
11838 const svn_wc__db_lock_t *lock,
11839 apr_pool_t *scratch_pool)
11841 svn_wc__db_wcroot_t *wcroot;
11842 const char *local_relpath;
11844 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11845 SVN_ERR_ASSERT(lock != NULL);
11847 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11848 local_abspath, scratch_pool, scratch_pool));
11849 VERIFY_USABLE_WCROOT(wcroot);
11851 SVN_WC__DB_WITH_TXN(
11852 lock_add_txn(wcroot, local_relpath, lock, scratch_pool),
11855 /* There may be some entries, and the lock info is now out of date. */
11856 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
11858 return SVN_NO_ERROR;
11862 /* The body of svn_wc__db_lock_remove().
11864 static svn_error_t *
11865 lock_remove_txn(svn_wc__db_wcroot_t *wcroot,
11866 const char *local_relpath,
11867 apr_pool_t *scratch_pool)
11869 const char *repos_relpath;
11870 apr_int64_t repos_id;
11871 svn_sqlite__stmt_t *stmt;
11873 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11874 &repos_relpath, &repos_id,
11875 NULL, NULL, NULL, NULL, NULL,
11876 NULL, NULL, NULL, NULL, NULL,
11877 wcroot, local_relpath,
11878 scratch_pool, scratch_pool));
11880 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11881 STMT_DELETE_LOCK));
11882 SVN_ERR(svn_sqlite__bindf(stmt, "is", repos_id, repos_relpath));
11884 SVN_ERR(svn_sqlite__step_done(stmt));
11886 return SVN_NO_ERROR;
11891 svn_wc__db_lock_remove(svn_wc__db_t *db,
11892 const char *local_abspath,
11893 apr_pool_t *scratch_pool)
11895 svn_wc__db_wcroot_t *wcroot;
11896 const char *local_relpath;
11898 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11900 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11901 local_abspath, scratch_pool, scratch_pool));
11902 VERIFY_USABLE_WCROOT(wcroot);
11904 SVN_WC__DB_WITH_TXN(
11905 lock_remove_txn(wcroot, local_relpath, scratch_pool),
11908 /* There may be some entries, and the lock info is now out of date. */
11909 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
11911 return SVN_NO_ERROR;
11916 svn_wc__db_scan_base_repos(const char **repos_relpath,
11917 const char **repos_root_url,
11918 const char **repos_uuid,
11920 const char *local_abspath,
11921 apr_pool_t *result_pool,
11922 apr_pool_t *scratch_pool)
11924 svn_wc__db_wcroot_t *wcroot;
11925 const char *local_relpath;
11926 apr_int64_t repos_id;
11928 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11930 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11931 local_abspath, scratch_pool, scratch_pool));
11932 VERIFY_USABLE_WCROOT(wcroot);
11934 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11935 repos_relpath, &repos_id,
11936 NULL, NULL, NULL, NULL, NULL,
11937 NULL, NULL, NULL, NULL, NULL,
11938 wcroot, local_relpath,
11939 result_pool, scratch_pool));
11940 SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot->sdb,
11941 repos_id, result_pool));
11943 return SVN_NO_ERROR;
11947 /* A helper for scan_addition().
11948 * Compute moved-from information for the node at LOCAL_RELPATH which
11949 * has been determined as having been moved-here.
11950 * If MOVED_FROM_RELPATH is not NULL, set *MOVED_FROM_RELPATH to the
11951 * path of the move-source node in *MOVED_FROM_RELPATH.
11952 * If DELETE_OP_ROOT_RELPATH is not NULL, set *DELETE_OP_ROOT_RELPATH
11953 * to the path of the op-root of the delete-half of the move.
11954 * If moved-from information cannot be derived, set both *MOVED_FROM_RELPATH
11955 * and *DELETE_OP_ROOT_RELPATH to NULL, and return a "copied" status.
11956 * COPY_OPT_ROOT_RELPATH is the relpath of the op-root of the copied-half
11958 static svn_error_t *
11959 get_moved_from_info(const char **moved_from_relpath,
11960 const char **moved_from_op_root_relpath,
11961 const char *moved_to_op_root_relpath,
11963 svn_wc__db_wcroot_t *wcroot,
11964 const char *local_relpath,
11965 apr_pool_t *result_pool,
11966 apr_pool_t *scratch_pool)
11968 svn_sqlite__stmt_t *stmt;
11969 svn_boolean_t have_row;
11971 /* Run a query to get the moved-from path from the DB. */
11972 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11973 STMT_SELECT_MOVED_FROM_RELPATH));
11974 SVN_ERR(svn_sqlite__bindf(stmt, "is",
11975 wcroot->wc_id, moved_to_op_root_relpath));
11976 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11980 /* The move was only recorded at the copy-half, possibly because
11981 * the move operation was interrupted mid-way between the copy
11982 * and the delete. Treat this node as a normal copy. */
11983 if (moved_from_relpath)
11984 *moved_from_relpath = NULL;
11985 if (moved_from_op_root_relpath)
11986 *moved_from_op_root_relpath = NULL;
11988 SVN_ERR(svn_sqlite__reset(stmt));
11989 return SVN_NO_ERROR;
11993 *op_depth = svn_sqlite__column_int(stmt, 1);
11995 if (moved_from_relpath || moved_from_op_root_relpath)
11997 const char *db_delete_op_root_relpath;
11999 /* The moved-from path from the DB is the relpath of
12000 * the op_root of the delete-half of the move. */
12001 db_delete_op_root_relpath = svn_sqlite__column_text(stmt, 0,
12003 if (moved_from_op_root_relpath)
12004 *moved_from_op_root_relpath = db_delete_op_root_relpath;
12006 if (moved_from_relpath)
12008 if (strcmp(moved_to_op_root_relpath, local_relpath) == 0)
12010 /* LOCAL_RELPATH is the op_root of the copied-half of the
12011 * move, so the correct MOVED_FROM_ABSPATH is the op-root
12012 * of the delete-half. */
12013 *moved_from_relpath = db_delete_op_root_relpath;
12017 const char *child_relpath;
12019 /* LOCAL_RELPATH is a child that was copied along with the
12020 * op_root of the copied-half of the move. Construct the
12021 * corresponding path beneath the op_root of the delete-half. */
12023 /* Grab the child path relative to the op_root of the move
12025 child_relpath = svn_relpath_skip_ancestor(
12026 moved_to_op_root_relpath, local_relpath);
12028 SVN_ERR_ASSERT(child_relpath && strlen(child_relpath) > 0);
12030 /* This join is valid because LOCAL_RELPATH has not been moved
12031 * within the copied-half of the move yet -- else, it would
12032 * be its own op_root. */
12033 *moved_from_relpath = svn_relpath_join(db_delete_op_root_relpath,
12040 SVN_ERR(svn_sqlite__reset(stmt));
12042 return SVN_NO_ERROR;
12045 /* The body of scan_addition().
12047 static svn_error_t *
12048 scan_addition_txn(svn_wc__db_status_t *status,
12049 const char **op_root_relpath_p,
12050 const char **repos_relpath,
12051 apr_int64_t *repos_id,
12052 const char **original_repos_relpath,
12053 apr_int64_t *original_repos_id,
12054 svn_revnum_t *original_revision,
12055 const char **moved_from_relpath,
12056 const char **moved_from_op_root_relpath,
12057 int *moved_from_op_depth,
12058 svn_wc__db_wcroot_t *wcroot,
12059 const char *local_relpath,
12060 apr_pool_t *result_pool,
12061 apr_pool_t *scratch_pool)
12063 const char *op_root_relpath;
12064 const char *build_relpath = "";
12066 /* Initialize most of the OUT parameters. Generally, we'll only be filling
12067 in a subset of these, so it is easier to init all up front. Note that
12068 the STATUS parameter will be initialized once we read the status of
12069 the specified node. */
12070 if (op_root_relpath_p)
12071 *op_root_relpath_p = NULL;
12072 if (original_repos_relpath)
12073 *original_repos_relpath = NULL;
12074 if (original_repos_id)
12075 *original_repos_id = INVALID_REPOS_ID;
12076 if (original_revision)
12077 *original_revision = SVN_INVALID_REVNUM;
12078 if (moved_from_relpath)
12079 *moved_from_relpath = NULL;
12080 if (moved_from_op_root_relpath)
12081 *moved_from_op_root_relpath = NULL;
12082 if (moved_from_op_depth)
12083 *moved_from_op_depth = 0;
12086 svn_sqlite__stmt_t *stmt;
12087 svn_boolean_t have_row;
12088 svn_wc__db_status_t presence;
12090 const char *repos_prefix_path = "";
12093 /* ### is it faster to fetch fewer columns? */
12094 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12095 STMT_SELECT_WORKING_NODE));
12096 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
12097 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12101 /* Reset statement before returning */
12102 SVN_ERR(svn_sqlite__reset(stmt));
12104 /* ### maybe we should return a usage error instead? */
12105 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12106 _("The node '%s' was not found."),
12107 path_for_error_message(wcroot,
12112 presence = svn_sqlite__column_token(stmt, 1, presence_map);
12114 /* The starting node should exist normally. */
12115 op_depth = svn_sqlite__column_int(stmt, 0);
12116 if (op_depth == 0 || (presence != svn_wc__db_status_normal
12117 && presence != svn_wc__db_status_incomplete))
12118 /* reset the statement as part of the error generation process */
12119 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
12120 svn_sqlite__reset(stmt),
12121 _("Expected node '%s' to be added."),
12122 path_for_error_message(wcroot,
12126 if (original_revision)
12127 *original_revision = svn_sqlite__column_revnum(stmt, 12);
12129 /* Provide the default status; we'll override as appropriate. */
12132 if (presence == svn_wc__db_status_normal)
12133 *status = svn_wc__db_status_added;
12135 *status = svn_wc__db_status_incomplete;
12139 /* Calculate the op root local path components */
12140 op_root_relpath = local_relpath;
12142 for (i = relpath_depth(local_relpath); i > op_depth; --i)
12144 /* Calculate the path of the operation root */
12145 repos_prefix_path =
12146 svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
12149 op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
12152 if (op_root_relpath_p)
12153 *op_root_relpath_p = apr_pstrdup(result_pool, op_root_relpath);
12155 /* ### This if-statement is quite redundant.
12156 * ### We're checking all these values again within the body anyway.
12157 * ### The body should be broken up appropriately and move into the
12158 * ### outer scope. */
12159 if (original_repos_relpath
12160 || original_repos_id
12161 || (original_revision
12162 && *original_revision == SVN_INVALID_REVNUM)
12164 || moved_from_relpath || moved_from_op_root_relpath)
12166 if (local_relpath != op_root_relpath)
12167 /* requery to get the add/copy root */
12169 SVN_ERR(svn_sqlite__reset(stmt));
12171 SVN_ERR(svn_sqlite__bindf(stmt, "is",
12172 wcroot->wc_id, op_root_relpath));
12173 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12177 /* Reset statement before returning */
12178 SVN_ERR(svn_sqlite__reset(stmt));
12180 /* ### maybe we should return a usage error instead? */
12181 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12182 _("The node '%s' was not found."),
12183 path_for_error_message(wcroot,
12188 if (original_revision
12189 && *original_revision == SVN_INVALID_REVNUM)
12190 *original_revision = svn_sqlite__column_revnum(stmt, 12);
12193 if (original_repos_relpath)
12194 *original_repos_relpath = svn_sqlite__column_text(stmt, 11,
12197 if (!svn_sqlite__column_is_null(stmt, 10)
12199 || original_repos_id
12200 || moved_from_relpath || moved_from_op_root_relpath))
12201 /* If column 10 (original_repos_id) is NULL,
12202 this is a plain add, not a copy or a move */
12204 svn_boolean_t moved_here;
12205 if (original_repos_id)
12206 *original_repos_id = svn_sqlite__column_int64(stmt, 10);
12208 moved_here = svn_sqlite__column_boolean(stmt, 13 /* moved_here */);
12210 *status = moved_here ? svn_wc__db_status_moved_here
12211 : svn_wc__db_status_copied;
12214 && (moved_from_relpath || moved_from_op_root_relpath))
12218 err = get_moved_from_info(moved_from_relpath,
12219 moved_from_op_root_relpath,
12221 moved_from_op_depth,
12222 wcroot, local_relpath,
12227 return svn_error_compose_create(
12228 err, svn_sqlite__reset(stmt));
12234 /* ### This loop here is to skip up to the first node which is a BASE node,
12235 because base_get_info() doesn't accommodate the scenario that
12236 we're looking at here; we found the true op_root, which may be inside
12237 further changed trees. */
12238 if (repos_relpath || repos_id)
12240 const char *base_relpath;
12245 SVN_ERR(svn_sqlite__reset(stmt));
12247 /* Pointing at op_depth, look at the parent */
12248 repos_prefix_path =
12249 svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
12252 op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
12255 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, op_root_relpath));
12256 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12261 op_depth = svn_sqlite__column_int(stmt, 0);
12263 /* Skip to op_depth */
12264 for (i = relpath_depth(op_root_relpath); i > op_depth; i--)
12266 /* Calculate the path of the operation root */
12267 repos_prefix_path =
12268 svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
12272 svn_relpath_dirname(op_root_relpath, scratch_pool);
12276 SVN_ERR(svn_sqlite__reset(stmt));
12278 build_relpath = repos_prefix_path;
12280 /* If we're here, then we have an added/copied/moved (start) node, and
12281 CURRENT_ABSPATH now points to a BASE node. Figure out the repository
12282 information for the current node, and use that to compute the start
12283 node's repository information. */
12284 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12285 &base_relpath, repos_id,
12286 NULL, NULL, NULL, NULL, NULL,
12287 NULL, NULL, NULL, NULL, NULL,
12288 wcroot, op_root_relpath,
12289 scratch_pool, scratch_pool));
12292 *repos_relpath = svn_relpath_join(base_relpath, build_relpath,
12296 SVN_ERR(svn_sqlite__reset(stmt));
12298 /* Postconditions */
12302 SVN_ERR_ASSERT(*status == svn_wc__db_status_added
12303 || *status == svn_wc__db_status_copied
12304 || *status == svn_wc__db_status_incomplete
12305 || *status == svn_wc__db_status_moved_here);
12306 if (*status == svn_wc__db_status_added)
12308 SVN_ERR_ASSERT(!original_repos_relpath
12309 || *original_repos_relpath == NULL);
12310 SVN_ERR_ASSERT(!original_revision
12311 || *original_revision == SVN_INVALID_REVNUM);
12312 SVN_ERR_ASSERT(!original_repos_id
12313 || *original_repos_id == INVALID_REPOS_ID);
12315 /* An upgrade with a missing directory can leave INCOMPLETE working
12316 op-roots. See upgrade_tests.py 29: upgrade with missing replaced dir
12318 else if (*status != svn_wc__db_status_incomplete)
12320 SVN_ERR_ASSERT(!original_repos_relpath
12321 || *original_repos_relpath != NULL);
12322 SVN_ERR_ASSERT(!original_revision
12323 || *original_revision != SVN_INVALID_REVNUM);
12324 SVN_ERR_ASSERT(!original_repos_id
12325 || *original_repos_id != INVALID_REPOS_ID);
12328 SVN_ERR_ASSERT(!op_root_relpath_p || *op_root_relpath_p != NULL);
12331 return SVN_NO_ERROR;
12335 /* Like svn_wc__db_scan_addition(), but with WCROOT+LOCAL_RELPATH instead of
12338 The output value of *ORIGINAL_REPOS_ID will be INVALID_REPOS_ID if there
12339 is no 'copy-from' repository. */
12340 static svn_error_t *
12341 scan_addition(svn_wc__db_status_t *status,
12342 const char **op_root_relpath,
12343 const char **repos_relpath,
12344 apr_int64_t *repos_id,
12345 const char **original_repos_relpath,
12346 apr_int64_t *original_repos_id,
12347 svn_revnum_t *original_revision,
12348 const char **moved_from_relpath,
12349 const char **moved_from_op_root_relpath,
12350 int *moved_from_op_depth,
12351 svn_wc__db_wcroot_t *wcroot,
12352 const char *local_relpath,
12353 apr_pool_t *result_pool,
12354 apr_pool_t *scratch_pool)
12356 SVN_WC__DB_WITH_TXN(
12357 scan_addition_txn(status, op_root_relpath, repos_relpath, repos_id,
12358 original_repos_relpath, original_repos_id,
12359 original_revision, moved_from_relpath,
12360 moved_from_op_root_relpath, moved_from_op_depth,
12361 wcroot, local_relpath, result_pool, scratch_pool),
12363 return SVN_NO_ERROR;
12368 svn_wc__db_scan_addition(svn_wc__db_status_t *status,
12369 const char **op_root_abspath,
12370 const char **repos_relpath,
12371 const char **repos_root_url,
12372 const char **repos_uuid,
12373 const char **original_repos_relpath,
12374 const char **original_root_url,
12375 const char **original_uuid,
12376 svn_revnum_t *original_revision,
12378 const char *local_abspath,
12379 apr_pool_t *result_pool,
12380 apr_pool_t *scratch_pool)
12382 svn_wc__db_wcroot_t *wcroot;
12383 const char *local_relpath;
12384 const char *op_root_relpath = NULL;
12385 apr_int64_t repos_id = INVALID_REPOS_ID;
12386 apr_int64_t original_repos_id = INVALID_REPOS_ID;
12387 apr_int64_t *repos_id_p
12388 = (repos_root_url || repos_uuid) ? &repos_id : NULL;
12389 apr_int64_t *original_repos_id_p
12390 = (original_root_url || original_uuid) ? &original_repos_id : NULL;
12392 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12394 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12395 local_abspath, scratch_pool, scratch_pool));
12396 VERIFY_USABLE_WCROOT(wcroot);
12398 SVN_ERR(scan_addition(status,
12402 repos_relpath, repos_id_p,
12403 original_repos_relpath, original_repos_id_p,
12406 wcroot, local_relpath, result_pool, scratch_pool));
12408 if (op_root_abspath)
12409 *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
12411 /* REPOS_ID must be valid if requested; ORIGINAL_REPOS_ID need not be. */
12412 SVN_ERR_ASSERT(repos_id_p == NULL || repos_id != INVALID_REPOS_ID);
12414 SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot->sdb,
12415 repos_id, result_pool));
12416 SVN_ERR(svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
12417 wcroot->sdb, original_repos_id,
12420 return SVN_NO_ERROR;
12424 svn_wc__db_scan_moved(const char **moved_from_abspath,
12425 const char **op_root_abspath,
12426 const char **op_root_moved_from_abspath,
12427 const char **moved_from_delete_abspath,
12429 const char *local_abspath,
12430 apr_pool_t *result_pool,
12431 apr_pool_t *scratch_pool)
12433 svn_wc__db_wcroot_t *wcroot;
12434 const char *local_relpath;
12435 svn_wc__db_status_t status;
12436 const char *op_root_relpath = NULL;
12437 const char *moved_from_relpath = NULL;
12438 const char *moved_from_op_root_relpath = NULL;
12439 int moved_from_op_depth = -1;
12441 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12443 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12444 local_abspath, scratch_pool, scratch_pool));
12445 VERIFY_USABLE_WCROOT(wcroot);
12447 SVN_ERR(scan_addition(&status,
12454 ? &moved_from_relpath
12456 (op_root_moved_from_abspath
12457 || moved_from_delete_abspath)
12458 ? &moved_from_op_root_relpath
12460 moved_from_delete_abspath
12461 ? &moved_from_op_depth
12463 wcroot, local_relpath, scratch_pool, scratch_pool));
12465 if (status != svn_wc__db_status_moved_here || !moved_from_relpath)
12466 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
12467 _("Path '%s' was not moved here"),
12468 path_for_error_message(wcroot, local_relpath,
12471 if (op_root_abspath)
12472 *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
12475 if (moved_from_abspath)
12476 *moved_from_abspath = svn_dirent_join(wcroot->abspath, moved_from_relpath,
12479 if (op_root_moved_from_abspath)
12480 *op_root_moved_from_abspath = svn_dirent_join(wcroot->abspath,
12481 moved_from_op_root_relpath,
12484 /* The deleted node is either where we moved from, or one of its ancestors */
12485 if (moved_from_delete_abspath)
12487 const char *tmp = moved_from_op_root_relpath;
12489 SVN_ERR_ASSERT(moved_from_op_depth >= 0);
12491 while (relpath_depth(tmp) > moved_from_op_depth)
12492 tmp = svn_relpath_dirname(tmp, scratch_pool);
12494 *moved_from_delete_abspath = svn_dirent_join(wcroot->abspath, tmp,
12498 return SVN_NO_ERROR;
12503 static svn_error_t *
12504 follow_moved_to(apr_array_header_t **moved_tos,
12506 const char *repos_path,
12507 svn_revnum_t revision,
12508 svn_wc__db_wcroot_t *wcroot,
12509 const char *local_relpath,
12510 apr_pool_t *result_pool,
12511 apr_pool_t *scratch_pool)
12513 svn_sqlite__stmt_t *stmt;
12514 svn_boolean_t have_row;
12515 int working_op_depth;
12516 const char *ancestor_relpath, *node_moved_to = NULL;
12519 SVN_ERR_ASSERT((!op_depth && !repos_path) || (op_depth && repos_path));
12521 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12522 STMT_SELECT_OP_DEPTH_MOVED_TO));
12523 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
12525 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12528 working_op_depth = svn_sqlite__column_int(stmt, 0);
12529 node_moved_to = svn_sqlite__column_text(stmt, 1, result_pool);
12532 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12533 if (!have_row || svn_sqlite__column_revnum(stmt, 0))
12534 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
12535 svn_sqlite__reset(stmt),
12536 _("The base node '%s' was not found."),
12537 path_for_error_message(wcroot,
12540 repos_path = svn_sqlite__column_text(stmt, 2, scratch_pool);
12541 revision = svn_sqlite__column_revnum(stmt, 3);
12544 SVN_ERR(svn_sqlite__reset(stmt));
12548 svn_boolean_t have_row2;
12550 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12551 STMT_SELECT_MOVED_HERE));
12552 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, node_moved_to,
12553 relpath_depth(node_moved_to)));
12554 SVN_ERR(svn_sqlite__step(&have_row2, stmt));
12555 if (!have_row2 || !svn_sqlite__column_int(stmt, 0)
12556 || revision != svn_sqlite__column_revnum(stmt, 3)
12557 || strcmp(repos_path, svn_sqlite__column_text(stmt, 2, NULL)))
12558 node_moved_to = NULL;
12559 SVN_ERR(svn_sqlite__reset(stmt));
12564 struct svn_wc__db_moved_to_t *moved_to;
12566 moved_to = apr_palloc(result_pool, sizeof(*moved_to));
12567 moved_to->op_depth = working_op_depth;
12568 moved_to->local_relpath = node_moved_to;
12569 APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
12572 /* A working row with moved_to, or no working row, and we are done. */
12573 if (node_moved_to || !have_row)
12574 return SVN_NO_ERROR;
12576 /* Need to handle being moved via an ancestor. */
12577 ancestor_relpath = local_relpath;
12578 for (i = relpath_depth(local_relpath); i > working_op_depth; --i)
12580 const char *ancestor_moved_to;
12582 ancestor_relpath = svn_relpath_dirname(ancestor_relpath, scratch_pool);
12584 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12585 STMT_SELECT_MOVED_TO));
12586 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, ancestor_relpath,
12587 working_op_depth));
12588 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12589 SVN_ERR_ASSERT(have_row);
12590 ancestor_moved_to = svn_sqlite__column_text(stmt, 0, scratch_pool);
12591 SVN_ERR(svn_sqlite__reset(stmt));
12592 if (ancestor_moved_to)
12595 = svn_relpath_join(ancestor_moved_to,
12596 svn_relpath_skip_ancestor(ancestor_relpath,
12600 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12601 STMT_SELECT_MOVED_HERE));
12602 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, node_moved_to,
12603 relpath_depth(ancestor_moved_to)));
12604 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12606 ancestor_moved_to = NULL;
12607 else if (!svn_sqlite__column_int(stmt, 0))
12609 svn_wc__db_status_t presence
12610 = svn_sqlite__column_token(stmt, 1, presence_map);
12611 if (presence != svn_wc__db_status_not_present)
12612 ancestor_moved_to = NULL;
12615 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12616 if (!have_row && !svn_sqlite__column_int(stmt, 0))
12617 ancestor_moved_to = NULL;
12620 SVN_ERR(svn_sqlite__reset(stmt));
12621 if (!ancestor_moved_to)
12623 /* verify repos_path points back? */
12625 if (ancestor_moved_to)
12627 struct svn_wc__db_moved_to_t *moved_to;
12629 moved_to = apr_palloc(result_pool, sizeof(*moved_to));
12630 moved_to->op_depth = working_op_depth;
12631 moved_to->local_relpath = node_moved_to;
12632 APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
12634 SVN_ERR(follow_moved_to(moved_tos, relpath_depth(ancestor_moved_to),
12635 repos_path, revision, wcroot, node_moved_to,
12636 result_pool, scratch_pool));
12641 return SVN_NO_ERROR;
12645 svn_wc__db_follow_moved_to(apr_array_header_t **moved_tos,
12647 const char *local_abspath,
12648 apr_pool_t *result_pool,
12649 apr_pool_t *scratch_pool)
12651 svn_wc__db_wcroot_t *wcroot;
12652 const char *local_relpath;
12654 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12656 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12657 local_abspath, scratch_pool, scratch_pool));
12658 VERIFY_USABLE_WCROOT(wcroot);
12660 *moved_tos = apr_array_make(result_pool, 0,
12661 sizeof(struct svn_wc__db_moved_to_t *));
12663 /* ### Wrap in a transaction */
12664 SVN_ERR(follow_moved_to(moved_tos, 0, NULL, SVN_INVALID_REVNUM,
12665 wcroot, local_relpath,
12666 result_pool, scratch_pool));
12668 /* ### Convert moved_to to abspath */
12670 return SVN_NO_ERROR;
12673 /* Extract the moved-to information for LOCAL_RELPATH at OP-DEPTH by
12674 examining the lowest working node above OP_DEPTH. The output paths
12675 are NULL if there is no move, otherwise:
12677 *MOVE_DST_RELPATH: the moved-to destination of LOCAL_RELPATH.
12679 *MOVE_DST_OP_ROOT_RELPATH: the moved-to destination of the root of
12680 the move of LOCAL_RELPATH. This may be equal to *MOVE_DST_RELPATH
12681 if LOCAL_RELPATH is the root of the move.
12683 *MOVE_SRC_ROOT_RELPATH: the root of the move source. For moves
12684 inside a delete this will be different from *MOVE_SRC_OP_ROOT_RELPATH.
12686 *MOVE_SRC_OP_ROOT_RELPATH: the root of the source layer that
12687 contains the move. For moves inside deletes this is the root of
12688 the delete, for other moves this is the root of the move.
12690 Given a path A/B/C with A/B moved to X then for A/B/C
12692 MOVE_DST_RELPATH is X/C
12693 MOVE_DST_OP_ROOT_RELPATH is X
12694 MOVE_SRC_ROOT_RELPATH is A/B
12695 MOVE_SRC_OP_ROOT_RELPATH is A/B
12697 If A is then deleted the MOVE_DST_RELPATH, MOVE_DST_OP_ROOT_RELPATH
12698 and MOVE_SRC_ROOT_RELPATH remain the same but MOVE_SRC_OP_ROOT_RELPATH
12701 ### Think about combining with scan_deletion? Also with
12702 ### scan_addition to get moved-to for replaces? Do we need to
12703 ### return the op-root of the move source, i.e. A/B in the example
12706 svn_wc__db_op_depth_moved_to(const char **move_dst_relpath,
12707 const char **move_dst_op_root_relpath,
12708 const char **move_src_root_relpath,
12709 const char **move_src_op_root_relpath,
12711 svn_wc__db_wcroot_t *wcroot,
12712 const char *local_relpath,
12713 apr_pool_t *result_pool,
12714 apr_pool_t *scratch_pool)
12716 svn_sqlite__stmt_t *stmt;
12717 svn_boolean_t have_row;
12718 int delete_op_depth;
12719 const char *relpath = local_relpath;
12721 *move_dst_relpath = *move_dst_op_root_relpath = NULL;
12722 *move_src_root_relpath = *move_src_op_root_relpath = NULL;
12726 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12727 STMT_SELECT_LOWEST_WORKING_NODE));
12728 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath, op_depth));
12729 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12732 delete_op_depth = svn_sqlite__column_int(stmt, 0);
12733 *move_dst_op_root_relpath = svn_sqlite__column_text(stmt, 3,
12735 if (*move_dst_op_root_relpath)
12736 *move_src_root_relpath = apr_pstrdup(result_pool, relpath);
12738 SVN_ERR(svn_sqlite__reset(stmt));
12739 if (!*move_dst_op_root_relpath)
12740 relpath = svn_relpath_dirname(relpath, scratch_pool);
12742 while (!*move_dst_op_root_relpath
12743 && have_row && delete_op_depth <= relpath_depth(relpath));
12745 if (*move_dst_op_root_relpath)
12748 = svn_relpath_join(*move_dst_op_root_relpath,
12749 svn_relpath_skip_ancestor(relpath, local_relpath),
12751 while (delete_op_depth < relpath_depth(relpath))
12752 relpath = svn_relpath_dirname(relpath, scratch_pool);
12753 *move_src_op_root_relpath = apr_pstrdup(result_pool, relpath);
12756 return SVN_NO_ERROR;
12759 /* Public (within libsvn_wc) absolute path version of
12760 svn_wc__db_op_depth_moved_to with the op-depth hard-coded to
12763 svn_wc__db_base_moved_to(const char **move_dst_abspath,
12764 const char **move_dst_op_root_abspath,
12765 const char **move_src_root_abspath,
12766 const char **move_src_op_root_abspath,
12768 const char *local_abspath,
12769 apr_pool_t *result_pool,
12770 apr_pool_t *scratch_pool)
12772 svn_wc__db_wcroot_t *wcroot;
12773 const char *local_relpath;
12774 const char *move_dst_relpath, *move_dst_op_root_relpath;
12775 const char *move_src_root_relpath, *move_src_op_root_relpath;
12777 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12779 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12780 local_abspath, scratch_pool, scratch_pool));
12781 VERIFY_USABLE_WCROOT(wcroot);
12783 SVN_WC__DB_WITH_TXN(svn_wc__db_op_depth_moved_to(&move_dst_relpath,
12784 &move_dst_op_root_relpath,
12785 &move_src_root_relpath,
12786 &move_src_op_root_relpath,
12787 0 /* BASE op-depth */,
12788 wcroot, local_relpath,
12789 scratch_pool, scratch_pool),
12792 if (move_dst_abspath)
12795 ? svn_dirent_join(wcroot->abspath, move_dst_relpath, result_pool)
12798 if (move_dst_op_root_abspath)
12799 *move_dst_op_root_abspath
12800 = move_dst_op_root_relpath
12801 ? svn_dirent_join(wcroot->abspath, move_dst_op_root_relpath, result_pool)
12804 if (move_src_root_abspath)
12805 *move_src_root_abspath
12806 = move_src_root_relpath
12807 ? svn_dirent_join(wcroot->abspath, move_src_root_relpath, result_pool)
12810 if (move_src_op_root_abspath)
12811 *move_src_op_root_abspath
12812 = move_src_op_root_relpath
12813 ? svn_dirent_join(wcroot->abspath, move_src_op_root_relpath, result_pool)
12816 return SVN_NO_ERROR;
12820 svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb,
12821 apr_int64_t *repos_id,
12822 apr_int64_t *wc_id,
12823 svn_wc__db_t *wc_db,
12824 const char *dir_abspath,
12825 const char *repos_root_url,
12826 const char *repos_uuid,
12827 apr_pool_t *scratch_pool)
12829 svn_wc__db_wcroot_t *wcroot;
12831 /* Upgrade is inherently exclusive so specify exclusive locking. */
12832 SVN_ERR(create_db(sdb, repos_id, wc_id, dir_abspath,
12833 repos_root_url, repos_uuid,
12835 NULL, SVN_INVALID_REVNUM, svn_depth_unknown,
12836 TRUE /* exclusive */,
12837 wc_db->state_pool, scratch_pool));
12839 SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
12840 apr_pstrdup(wc_db->state_pool,
12842 *sdb, *wc_id, FORMAT_FROM_SDB,
12843 FALSE /* auto-upgrade */,
12844 FALSE /* enforce_empty_wq */,
12845 wc_db->state_pool, scratch_pool));
12847 /* The WCROOT is complete. Stash it into DB. */
12848 svn_hash_sets(wc_db->dir_data, wcroot->abspath, wcroot);
12850 return SVN_NO_ERROR;
12855 svn_wc__db_upgrade_apply_dav_cache(svn_sqlite__db_t *sdb,
12856 const char *dir_relpath,
12857 apr_hash_t *cache_values,
12858 apr_pool_t *scratch_pool)
12860 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
12862 apr_hash_index_t *hi;
12863 svn_sqlite__stmt_t *stmt;
12865 SVN_ERR(svn_wc__db_util_fetch_wc_id(&wc_id, sdb, iterpool));
12867 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12868 STMT_UPDATE_BASE_NODE_DAV_CACHE));
12870 /* Iterate over all the wcprops, writing each one to the wc_db. */
12871 for (hi = apr_hash_first(scratch_pool, cache_values);
12873 hi = apr_hash_next(hi))
12875 const char *name = svn__apr_hash_index_key(hi);
12876 apr_hash_t *props = svn__apr_hash_index_val(hi);
12877 const char *local_relpath;
12879 svn_pool_clear(iterpool);
12881 local_relpath = svn_relpath_join(dir_relpath, name, iterpool);
12883 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
12884 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, iterpool));
12885 SVN_ERR(svn_sqlite__step_done(stmt));
12888 svn_pool_destroy(iterpool);
12890 return SVN_NO_ERROR;
12895 svn_wc__db_upgrade_apply_props(svn_sqlite__db_t *sdb,
12896 const char *dir_abspath,
12897 const char *local_relpath,
12898 apr_hash_t *base_props,
12899 apr_hash_t *revert_props,
12900 apr_hash_t *working_props,
12901 int original_format,
12903 apr_pool_t *scratch_pool)
12905 svn_sqlite__stmt_t *stmt;
12906 svn_boolean_t have_row;
12907 int top_op_depth = -1;
12908 int below_op_depth = -1;
12909 svn_wc__db_status_t top_presence;
12910 svn_wc__db_status_t below_presence;
12913 /* ### working_props: use set_props_txn.
12914 ### if working_props == NULL, then skip. what if they equal the
12915 ### pristine props? we should probably do the compare here.
12917 ### base props go into WORKING_NODE if avail, otherwise BASE.
12919 ### revert only goes into BASE. (and WORKING better be there!)
12921 Prior to 1.4.0 (ORIGINAL_FORMAT < 8), REVERT_PROPS did not exist. If a
12922 file was deleted, then a copy (potentially with props) was disallowed
12923 and could not replace the deletion. An addition *could* be performed,
12924 but that would never bring its own props.
12926 1.4.0 through 1.4.5 created the concept of REVERT_PROPS, but had a
12927 bug in svn_wc_add_repos_file2() whereby a copy-with-props did NOT
12928 construct a REVERT_PROPS if the target had no props. Thus, reverting
12929 the delete/copy would see no REVERT_PROPS to restore, leaving the
12930 props from the copy source intact, and appearing as if they are (now)
12931 the base props for the previously-deleted file. (wc corruption)
12933 1.4.6 ensured that an empty REVERT_PROPS would be established at all
12934 times. See issue 2530, and r861670 as starting points.
12936 We will use ORIGINAL_FORMAT and SVN_WC__NO_REVERT_FILES to determine
12937 the handling of our inputs, relative to the state of this node.
12940 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_NODE_INFO));
12941 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
12942 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12945 top_op_depth = svn_sqlite__column_int(stmt, 0);
12946 top_presence = svn_sqlite__column_token(stmt, 3, presence_map);
12947 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12950 below_op_depth = svn_sqlite__column_int(stmt, 0);
12951 below_presence = svn_sqlite__column_token(stmt, 3, presence_map);
12954 SVN_ERR(svn_sqlite__reset(stmt));
12956 /* Detect the buggy scenario described above. We cannot upgrade this
12957 working copy if we have no idea where BASE_PROPS should go. */
12958 if (original_format > SVN_WC__NO_REVERT_FILES
12959 && revert_props == NULL
12960 && top_op_depth != -1
12961 && top_presence == svn_wc__db_status_normal
12962 && below_op_depth != -1
12963 && below_presence != svn_wc__db_status_not_present)
12965 /* There should be REVERT_PROPS, so it appears that we just ran into
12966 the described bug. Sigh. */
12967 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
12968 _("The properties of '%s' are in an "
12969 "indeterminate state and cannot be "
12970 "upgraded. See issue #2530."),
12971 svn_dirent_local_style(
12972 svn_dirent_join(dir_abspath, local_relpath,
12973 scratch_pool), scratch_pool));
12976 /* Need at least one row, or two rows if there are revert props */
12977 if (top_op_depth == -1
12978 || (below_op_depth == -1 && revert_props))
12979 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
12980 _("Insufficient NODES rows for '%s'"),
12981 svn_dirent_local_style(
12982 svn_dirent_join(dir_abspath, local_relpath,
12983 scratch_pool), scratch_pool));
12985 /* one row, base props only: upper row gets base props
12986 two rows, base props only: lower row gets base props
12987 two rows, revert props only: lower row gets revert props
12988 two rows, base and revert props: upper row gets base, lower gets revert */
12991 if (revert_props || below_op_depth == -1)
12993 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12994 STMT_UPDATE_NODE_PROPS));
12995 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
12996 wc_id, local_relpath, top_op_depth));
12997 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, base_props, scratch_pool));
12998 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
13000 SVN_ERR_ASSERT(affected_rows == 1);
13003 if (below_op_depth != -1)
13005 apr_hash_t *props = revert_props ? revert_props : base_props;
13007 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
13008 STMT_UPDATE_NODE_PROPS));
13009 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
13010 wc_id, local_relpath, below_op_depth));
13011 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
13012 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
13014 SVN_ERR_ASSERT(affected_rows == 1);
13017 /* If there are WORKING_PROPS, then they always go into ACTUAL_NODE. */
13018 if (working_props != NULL
13019 && base_props != NULL)
13021 apr_array_header_t *diffs;
13023 SVN_ERR(svn_prop_diffs(&diffs, working_props, base_props, scratch_pool));
13025 if (diffs->nelts == 0)
13026 working_props = NULL; /* No differences */
13029 if (working_props != NULL)
13031 SVN_ERR(set_actual_props(wc_id, local_relpath, working_props,
13032 sdb, scratch_pool));
13035 return SVN_NO_ERROR;
13039 svn_wc__db_upgrade_insert_external(svn_wc__db_t *db,
13040 const char *local_abspath,
13041 svn_node_kind_t kind,
13042 const char *parent_abspath,
13043 const char *def_local_abspath,
13044 const char *repos_relpath,
13045 const char *repos_root_url,
13046 const char *repos_uuid,
13047 svn_revnum_t def_peg_revision,
13048 svn_revnum_t def_revision,
13049 apr_pool_t *scratch_pool)
13051 svn_wc__db_wcroot_t *wcroot;
13052 const char *def_local_relpath;
13053 svn_sqlite__stmt_t *stmt;
13054 svn_boolean_t have_row;
13055 apr_int64_t repos_id;
13057 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13059 /* We know only of DEF_LOCAL_ABSPATH that it definitely belongs to "this"
13060 * WC, i.e. where the svn:externals prop is set. The external target path
13061 * itself may be "hidden behind" other working copies. */
13062 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &def_local_relpath,
13063 db, def_local_abspath,
13064 scratch_pool, scratch_pool));
13065 VERIFY_USABLE_WCROOT(wcroot);
13068 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13069 STMT_SELECT_REPOSITORY));
13070 SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
13071 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13074 repos_id = svn_sqlite__column_int64(stmt, 0);
13075 SVN_ERR(svn_sqlite__reset(stmt));
13079 /* Need to set up a new repository row. */
13080 SVN_ERR(create_repos_id(&repos_id, repos_root_url, repos_uuid,
13081 wcroot->sdb, scratch_pool));
13084 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13085 STMT_INSERT_EXTERNAL));
13087 /* wc_id, local_relpath, parent_relpath, presence, kind, def_local_relpath,
13088 * repos_id, def_repos_relpath, def_operational_revision, def_revision */
13089 SVN_ERR(svn_sqlite__bindf(stmt, "issstsis",
13091 svn_dirent_skip_ancestor(wcroot->abspath,
13093 svn_dirent_skip_ancestor(wcroot->abspath,
13101 if (SVN_IS_VALID_REVNUM(def_peg_revision))
13102 SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, def_peg_revision));
13104 if (SVN_IS_VALID_REVNUM(def_revision))
13105 SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, def_revision));
13107 SVN_ERR(svn_sqlite__insert(NULL, stmt));
13109 return SVN_NO_ERROR;
13113 svn_wc__db_upgrade_get_repos_id(apr_int64_t *repos_id,
13114 svn_sqlite__db_t *sdb,
13115 const char *repos_root_url,
13116 apr_pool_t *scratch_pool)
13118 svn_sqlite__stmt_t *stmt;
13119 svn_boolean_t have_row;
13121 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_REPOSITORY));
13122 SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
13123 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13126 return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt),
13127 _("Repository '%s' not found in the database"),
13130 *repos_id = svn_sqlite__column_int64(stmt, 0);
13131 return svn_error_trace(svn_sqlite__reset(stmt));
13136 svn_wc__db_wq_add(svn_wc__db_t *db,
13137 const char *wri_abspath,
13138 const svn_skel_t *work_item,
13139 apr_pool_t *scratch_pool)
13141 svn_wc__db_wcroot_t *wcroot;
13142 const char *local_relpath;
13144 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13146 /* Quick exit, if there are no work items to queue up. */
13147 if (work_item == NULL)
13148 return SVN_NO_ERROR;
13150 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13151 wri_abspath, scratch_pool, scratch_pool));
13152 VERIFY_USABLE_WCROOT(wcroot);
13154 /* Add the work item(s) to the WORK_QUEUE. */
13155 return svn_error_trace(add_work_items(wcroot->sdb, work_item,
13159 /* The body of svn_wc__db_wq_fetch_next().
13161 static svn_error_t *
13162 wq_fetch_next(apr_uint64_t *id,
13163 svn_skel_t **work_item,
13164 svn_wc__db_wcroot_t *wcroot,
13165 const char *local_relpath,
13166 apr_uint64_t completed_id,
13167 apr_pool_t *result_pool,
13168 apr_pool_t *scratch_pool)
13170 svn_sqlite__stmt_t *stmt;
13171 svn_boolean_t have_row;
13173 if (completed_id != 0)
13175 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13176 STMT_DELETE_WORK_ITEM));
13177 SVN_ERR(svn_sqlite__bind_int64(stmt, 1, completed_id));
13179 SVN_ERR(svn_sqlite__step_done(stmt));
13182 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13183 STMT_SELECT_WORK_ITEM));
13184 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13196 *id = svn_sqlite__column_int64(stmt, 0);
13198 val = svn_sqlite__column_blob(stmt, 1, &len, result_pool);
13200 *work_item = svn_skel__parse(val, len, result_pool);
13203 return svn_error_trace(svn_sqlite__reset(stmt));
13207 svn_wc__db_wq_fetch_next(apr_uint64_t *id,
13208 svn_skel_t **work_item,
13210 const char *wri_abspath,
13211 apr_uint64_t completed_id,
13212 apr_pool_t *result_pool,
13213 apr_pool_t *scratch_pool)
13215 svn_wc__db_wcroot_t *wcroot;
13216 const char *local_relpath;
13218 SVN_ERR_ASSERT(id != NULL);
13219 SVN_ERR_ASSERT(work_item != NULL);
13220 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13222 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13223 wri_abspath, scratch_pool, scratch_pool));
13224 VERIFY_USABLE_WCROOT(wcroot);
13226 SVN_WC__DB_WITH_TXN(
13227 wq_fetch_next(id, work_item,
13228 wcroot, local_relpath, completed_id,
13229 result_pool, scratch_pool),
13232 return SVN_NO_ERROR;
13235 /* Records timestamp and date for one or more files in wcroot */
13236 static svn_error_t *
13237 wq_record(svn_wc__db_wcroot_t *wcroot,
13238 apr_hash_t *record_map,
13239 apr_pool_t *scratch_pool)
13241 apr_hash_index_t *hi;
13242 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
13244 for (hi = apr_hash_first(scratch_pool, record_map); hi;
13245 hi = apr_hash_next(hi))
13247 const char *local_abspath = svn__apr_hash_index_key(hi);
13248 const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
13249 const char *local_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
13252 svn_pool_clear(iterpool);
13254 if (! local_relpath)
13257 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
13258 dirent->filesize, dirent->mtime,
13262 svn_pool_destroy(iterpool);
13263 return SVN_NO_ERROR;
13267 svn_wc__db_wq_record_and_fetch_next(apr_uint64_t *id,
13268 svn_skel_t **work_item,
13270 const char *wri_abspath,
13271 apr_uint64_t completed_id,
13272 apr_hash_t *record_map,
13273 apr_pool_t *result_pool,
13274 apr_pool_t *scratch_pool)
13276 svn_wc__db_wcroot_t *wcroot;
13277 const char *local_relpath;
13279 SVN_ERR_ASSERT(id != NULL);
13280 SVN_ERR_ASSERT(work_item != NULL);
13281 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13283 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13284 wri_abspath, scratch_pool, scratch_pool));
13285 VERIFY_USABLE_WCROOT(wcroot);
13287 SVN_WC__DB_WITH_TXN(
13288 svn_error_compose_create(
13289 wq_fetch_next(id, work_item,
13290 wcroot, local_relpath, completed_id,
13291 result_pool, scratch_pool),
13292 wq_record(wcroot, record_map, scratch_pool)),
13295 return SVN_NO_ERROR;
13300 /* ### temporary API. remove before release. */
13302 svn_wc__db_temp_get_format(int *format,
13304 const char *local_dir_abspath,
13305 apr_pool_t *scratch_pool)
13307 svn_wc__db_wcroot_t *wcroot;
13308 const char *local_relpath;
13311 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13312 /* ### assert that we were passed a directory? */
13314 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13315 local_dir_abspath, scratch_pool, scratch_pool);
13317 /* If we hit an error examining this directory, then declare this
13318 directory to not be a working copy. */
13321 if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
13322 return svn_error_trace(err);
13323 svn_error_clear(err);
13325 /* Remap the returned error. */
13327 return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
13328 _("'%s' is not a working copy"),
13329 svn_dirent_local_style(local_dir_abspath,
13333 SVN_ERR_ASSERT(wcroot != NULL);
13334 SVN_ERR_ASSERT(wcroot->format >= 1);
13336 *format = wcroot->format;
13338 return SVN_NO_ERROR;
13341 /* ### temporary API. remove before release. */
13342 svn_wc_adm_access_t *
13343 svn_wc__db_temp_get_access(svn_wc__db_t *db,
13344 const char *local_dir_abspath,
13345 apr_pool_t *scratch_pool)
13347 const char *local_relpath;
13348 svn_wc__db_wcroot_t *wcroot;
13351 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13353 /* ### we really need to assert that we were passed a directory. sometimes
13354 ### adm_retrieve_internal is asked about a file, and then it asks us
13355 ### for an access baton for it. we should definitely return NULL, but
13356 ### ideally: the caller would never ask us about a non-directory. */
13358 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13359 db, local_dir_abspath, scratch_pool, scratch_pool);
13362 svn_error_clear(err);
13369 return svn_hash_gets(wcroot->access_cache, local_dir_abspath);
13373 /* ### temporary API. remove before release. */
13375 svn_wc__db_temp_set_access(svn_wc__db_t *db,
13376 const char *local_dir_abspath,
13377 svn_wc_adm_access_t *adm_access,
13378 apr_pool_t *scratch_pool)
13380 const char *local_relpath;
13381 svn_wc__db_wcroot_t *wcroot;
13384 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13385 /* ### assert that we were passed a directory? */
13387 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13388 db, local_dir_abspath, scratch_pool, scratch_pool);
13391 /* We don't even have a wcroot, so just bail. */
13392 svn_error_clear(err);
13396 /* Better not override something already there. */
13397 SVN_ERR_ASSERT_NO_RETURN(
13398 svn_hash_gets(wcroot->access_cache, local_dir_abspath) == NULL
13400 svn_hash_sets(wcroot->access_cache, local_dir_abspath, adm_access);
13404 /* ### temporary API. remove before release. */
13406 svn_wc__db_temp_close_access(svn_wc__db_t *db,
13407 const char *local_dir_abspath,
13408 svn_wc_adm_access_t *adm_access,
13409 apr_pool_t *scratch_pool)
13411 const char *local_relpath;
13412 svn_wc__db_wcroot_t *wcroot;
13414 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13415 /* ### assert that we were passed a directory? */
13417 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13418 local_dir_abspath, scratch_pool, scratch_pool));
13419 svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13421 return SVN_NO_ERROR;
13425 /* ### temporary API. remove before release. */
13427 svn_wc__db_temp_clear_access(svn_wc__db_t *db,
13428 const char *local_dir_abspath,
13429 apr_pool_t *scratch_pool)
13431 const char *local_relpath;
13432 svn_wc__db_wcroot_t *wcroot;
13435 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13436 /* ### assert that we were passed a directory? */
13438 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13439 db, local_dir_abspath, scratch_pool, scratch_pool);
13442 svn_error_clear(err);
13446 svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13451 svn_wc__db_temp_get_all_access(svn_wc__db_t *db,
13452 apr_pool_t *result_pool)
13454 apr_hash_t *result = apr_hash_make(result_pool);
13455 apr_hash_index_t *hi;
13457 for (hi = apr_hash_first(result_pool, db->dir_data);
13459 hi = apr_hash_next(hi))
13461 const svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi);
13463 /* This is highly redundant, 'cause the same WCROOT will appear many
13464 times in dir_data. */
13465 result = apr_hash_overlay(result_pool, result, wcroot->access_cache);
13473 svn_wc__db_temp_borrow_sdb(svn_sqlite__db_t **sdb,
13475 const char *local_dir_abspath,
13476 apr_pool_t *scratch_pool)
13478 svn_wc__db_wcroot_t *wcroot;
13479 const char *local_relpath;
13481 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13483 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13484 local_dir_abspath, scratch_pool, scratch_pool));
13485 VERIFY_USABLE_WCROOT(wcroot);
13487 *sdb = wcroot->sdb;
13489 return SVN_NO_ERROR;
13494 svn_wc__db_read_conflict_victims(const apr_array_header_t **victims,
13496 const char *local_abspath,
13497 apr_pool_t *result_pool,
13498 apr_pool_t *scratch_pool)
13500 svn_wc__db_wcroot_t *wcroot;
13501 const char *local_relpath;
13502 svn_sqlite__stmt_t *stmt;
13503 svn_boolean_t have_row;
13504 apr_array_header_t *new_victims;
13506 /* The parent should be a working copy directory. */
13507 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13508 local_abspath, scratch_pool, scratch_pool));
13509 VERIFY_USABLE_WCROOT(wcroot);
13511 /* ### This will be much easier once we have all conflicts in one
13514 /* Look for text, tree and property conflicts in ACTUAL */
13515 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13516 STMT_SELECT_CONFLICT_VICTIMS));
13517 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13519 new_victims = apr_array_make(result_pool, 0, sizeof(const char *));
13521 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13524 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
13526 APR_ARRAY_PUSH(new_victims, const char *) =
13527 svn_relpath_basename(child_relpath, result_pool);
13529 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13532 SVN_ERR(svn_sqlite__reset(stmt));
13534 *victims = new_victims;
13535 return SVN_NO_ERROR;
13538 /* The body of svn_wc__db_get_conflict_marker_files().
13540 static svn_error_t *
13541 get_conflict_marker_files(apr_hash_t **marker_files_p,
13542 svn_wc__db_wcroot_t *wcroot,
13543 const char *local_relpath,
13545 apr_pool_t *result_pool,
13546 apr_pool_t *scratch_pool)
13548 svn_sqlite__stmt_t *stmt;
13549 svn_boolean_t have_row;
13550 apr_hash_t *marker_files = apr_hash_make(result_pool);
13552 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13553 STMT_SELECT_ACTUAL_NODE));
13554 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13555 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13557 if (have_row && !svn_sqlite__column_is_null(stmt, 2))
13560 const void *data = svn_sqlite__column_blob(stmt, 2, &len, NULL);
13561 svn_skel_t *conflicts;
13562 const apr_array_header_t *markers;
13565 conflicts = svn_skel__parse(data, len, scratch_pool);
13567 /* ### ADD markers to *marker_files */
13568 SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13570 result_pool, scratch_pool));
13572 for (i = 0; markers && (i < markers->nelts); i++)
13574 const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13576 svn_hash_sets(marker_files, marker_abspath, "");
13579 SVN_ERR(svn_sqlite__reset(stmt));
13581 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13582 STMT_SELECT_CONFLICT_VICTIMS));
13583 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13584 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13589 const void *data = svn_sqlite__column_blob(stmt, 1, &len, NULL);
13591 const apr_array_header_t *markers;
13596 svn_skel_t *conflicts;
13597 conflicts = svn_skel__parse(data, len, scratch_pool);
13599 SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13601 result_pool, scratch_pool));
13603 for (i = 0; markers && (i < markers->nelts); i++)
13605 const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13607 svn_hash_sets(marker_files, marker_abspath, "");
13611 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13614 if (apr_hash_count(marker_files))
13615 *marker_files_p = marker_files;
13617 *marker_files_p = NULL;
13619 return svn_error_trace(svn_sqlite__reset(stmt));
13623 svn_wc__db_get_conflict_marker_files(apr_hash_t **marker_files,
13625 const char *local_abspath,
13626 apr_pool_t *result_pool,
13627 apr_pool_t *scratch_pool)
13629 svn_wc__db_wcroot_t *wcroot;
13630 const char *local_relpath;
13632 /* The parent should be a working copy directory. */
13633 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13634 local_abspath, scratch_pool, scratch_pool));
13635 VERIFY_USABLE_WCROOT(wcroot);
13637 SVN_WC__DB_WITH_TXN(
13638 get_conflict_marker_files(marker_files, wcroot, local_relpath, db,
13639 result_pool, scratch_pool),
13642 return SVN_NO_ERROR;
13647 svn_wc__db_read_conflict(svn_skel_t **conflict,
13649 const char *local_abspath,
13650 apr_pool_t *result_pool,
13651 apr_pool_t *scratch_pool)
13653 svn_wc__db_wcroot_t *wcroot;
13654 const char *local_relpath;
13656 /* The parent should be a working copy directory. */
13657 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13658 local_abspath, scratch_pool, scratch_pool));
13659 VERIFY_USABLE_WCROOT(wcroot);
13661 return svn_error_trace(svn_wc__db_read_conflict_internal(conflict, wcroot,
13668 svn_wc__db_read_conflict_internal(svn_skel_t **conflict,
13669 svn_wc__db_wcroot_t *wcroot,
13670 const char *local_relpath,
13671 apr_pool_t *result_pool,
13672 apr_pool_t *scratch_pool)
13674 svn_sqlite__stmt_t *stmt;
13675 svn_boolean_t have_row;
13677 /* Check if we have a conflict in ACTUAL */
13678 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13679 STMT_SELECT_ACTUAL_NODE));
13680 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13682 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13686 /* Do this while stmt is still open to avoid closing the sqlite
13687 transaction and then reopening. */
13688 svn_sqlite__stmt_t *stmt_node;
13691 err = svn_sqlite__get_statement(&stmt_node, wcroot->sdb,
13692 STMT_SELECT_NODE_INFO);
13697 err = svn_sqlite__bindf(stmt_node, "is", wcroot->wc_id,
13701 err = svn_sqlite__step(&have_row, stmt_node);
13704 err = svn_error_compose_create(err,
13705 svn_sqlite__reset(stmt_node));
13707 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
13712 return SVN_NO_ERROR;
13715 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13716 _("The node '%s' was not found."),
13717 path_for_error_message(wcroot,
13723 apr_size_t cfl_len;
13724 const void *cfl_data;
13726 /* svn_skel__parse doesn't copy data, so store in result_pool */
13727 cfl_data = svn_sqlite__column_blob(stmt, 2, &cfl_len, result_pool);
13730 *conflict = svn_skel__parse(cfl_data, cfl_len, result_pool);
13734 return svn_error_trace(svn_sqlite__reset(stmt));
13740 svn_wc__db_read_kind(svn_node_kind_t *kind,
13742 const char *local_abspath,
13743 svn_boolean_t allow_missing,
13744 svn_boolean_t show_deleted,
13745 svn_boolean_t show_hidden,
13746 apr_pool_t *scratch_pool)
13748 svn_wc__db_wcroot_t *wcroot;
13749 const char *local_relpath;
13750 svn_sqlite__stmt_t *stmt_info;
13751 svn_boolean_t have_info;
13753 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13755 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13756 local_abspath, scratch_pool, scratch_pool));
13757 VERIFY_USABLE_WCROOT(wcroot);
13759 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
13760 STMT_SELECT_NODE_INFO));
13761 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
13762 SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
13768 *kind = svn_node_unknown;
13769 SVN_ERR(svn_sqlite__reset(stmt_info));
13770 return SVN_NO_ERROR;
13774 SVN_ERR(svn_sqlite__reset(stmt_info));
13775 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13776 _("The node '%s' was not found."),
13777 path_for_error_message(wcroot,
13783 if (!(show_deleted && show_hidden))
13785 int op_depth = svn_sqlite__column_int(stmt_info, 0);
13786 svn_boolean_t report_none = FALSE;
13787 svn_wc__db_status_t status = svn_sqlite__column_token(stmt_info, 3,
13791 SVN_ERR(convert_to_working_status(&status, status));
13795 case svn_wc__db_status_not_present:
13796 if (! (show_hidden && show_deleted))
13797 report_none = TRUE;
13799 case svn_wc__db_status_excluded:
13800 case svn_wc__db_status_server_excluded:
13802 report_none = TRUE;
13804 case svn_wc__db_status_deleted:
13805 if (! show_deleted)
13806 report_none = TRUE;
13814 *kind = svn_node_none;
13815 return svn_error_trace(svn_sqlite__reset(stmt_info));
13819 *kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
13821 return svn_error_trace(svn_sqlite__reset(stmt_info));
13826 svn_wc__db_node_hidden(svn_boolean_t *hidden,
13828 const char *local_abspath,
13829 apr_pool_t *scratch_pool)
13831 svn_wc__db_wcroot_t *wcroot;
13832 const char *local_relpath;
13833 svn_wc__db_status_t status;
13835 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13837 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13838 local_abspath, scratch_pool, scratch_pool));
13839 VERIFY_USABLE_WCROOT(wcroot);
13841 SVN_ERR(read_info(&status, NULL, NULL, NULL, NULL, NULL,
13842 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13843 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13845 wcroot, local_relpath,
13846 scratch_pool, scratch_pool));
13848 *hidden = (status == svn_wc__db_status_server_excluded
13849 || status == svn_wc__db_status_not_present
13850 || status == svn_wc__db_status_excluded);
13852 return SVN_NO_ERROR;
13857 svn_wc__db_is_wcroot(svn_boolean_t *is_wcroot,
13859 const char *local_abspath,
13860 apr_pool_t *scratch_pool)
13862 svn_wc__db_wcroot_t *wcroot;
13863 const char *local_relpath;
13865 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13867 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13868 local_abspath, scratch_pool, scratch_pool));
13869 VERIFY_USABLE_WCROOT(wcroot);
13871 if (*local_relpath != '\0')
13873 *is_wcroot = FALSE; /* Node is a file, or has a parent directory within
13875 return SVN_NO_ERROR;
13880 return SVN_NO_ERROR;
13883 /* Find a node's kind and whether it is switched, putting the outputs in
13884 * *IS_SWITCHED and *KIND. Either of the outputs may be NULL if not wanted.
13886 static svn_error_t *
13887 db_is_switched(svn_boolean_t *is_switched,
13888 svn_node_kind_t *kind,
13889 svn_wc__db_wcroot_t *wcroot,
13890 const char *local_relpath,
13891 apr_pool_t *scratch_pool)
13893 svn_wc__db_status_t status;
13894 apr_int64_t repos_id;
13895 const char *repos_relpath;
13897 const char *parent_local_relpath;
13898 apr_int64_t parent_repos_id;
13899 const char *parent_repos_relpath;
13901 SVN_ERR_ASSERT(*local_relpath != '\0'); /* Handled in wrapper */
13903 SVN_ERR(read_info(&status, kind, NULL, &repos_relpath, &repos_id, NULL,
13904 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13905 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13906 wcroot, local_relpath, scratch_pool, scratch_pool));
13908 if (status == svn_wc__db_status_server_excluded
13909 || status == svn_wc__db_status_excluded
13910 || status == svn_wc__db_status_not_present)
13912 return svn_error_createf(
13913 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13914 _("The node '%s' was not found."),
13915 path_for_error_message(wcroot, local_relpath,
13918 else if (! repos_relpath)
13920 /* Node is shadowed; easy out */
13922 *is_switched = FALSE;
13924 return SVN_NO_ERROR;
13928 return SVN_NO_ERROR;
13930 svn_relpath_split(&parent_local_relpath, &name, local_relpath, scratch_pool);
13932 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
13933 &parent_repos_relpath,
13934 &parent_repos_id, NULL, NULL, NULL,
13935 NULL, NULL, NULL, NULL, NULL,
13937 wcroot, parent_local_relpath,
13938 scratch_pool, scratch_pool));
13940 if (repos_id != parent_repos_id)
13941 *is_switched = TRUE;
13944 const char *expected_relpath;
13946 expected_relpath = svn_relpath_join(parent_repos_relpath, name,
13949 *is_switched = (strcmp(expected_relpath, repos_relpath) != 0);
13952 return SVN_NO_ERROR;
13956 svn_wc__db_is_switched(svn_boolean_t *is_wcroot,
13957 svn_boolean_t *is_switched,
13958 svn_node_kind_t *kind,
13960 const char *local_abspath,
13961 apr_pool_t *scratch_pool)
13963 svn_wc__db_wcroot_t *wcroot;
13964 const char *local_relpath;
13966 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13968 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13969 local_abspath, scratch_pool, scratch_pool));
13970 VERIFY_USABLE_WCROOT(wcroot);
13973 *is_switched = FALSE;
13975 if (*local_relpath == '\0')
13982 *kind = svn_node_dir;
13983 return SVN_NO_ERROR;
13987 *is_wcroot = FALSE;
13989 if (! is_switched && ! kind)
13990 return SVN_NO_ERROR;
13992 SVN_WC__DB_WITH_TXN(
13993 db_is_switched(is_switched, kind, wcroot, local_relpath, scratch_pool),
13995 return SVN_NO_ERROR;
14000 svn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath,
14002 const char *wri_abspath,
14003 apr_pool_t *result_pool,
14004 apr_pool_t *scratch_pool)
14006 svn_wc__db_wcroot_t *wcroot;
14007 const char *local_relpath;
14009 SVN_ERR_ASSERT(temp_dir_abspath != NULL);
14010 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
14012 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14013 wri_abspath, scratch_pool, scratch_pool));
14014 VERIFY_USABLE_WCROOT(wcroot);
14016 *temp_dir_abspath = svn_dirent_join_many(result_pool,
14018 svn_wc_get_adm_dir(scratch_pool),
14019 WCROOT_TEMPDIR_RELPATH,
14021 return SVN_NO_ERROR;
14025 /* Helper for wclock_obtain_cb() to steal an existing lock */
14026 static svn_error_t *
14027 wclock_steal(svn_wc__db_wcroot_t *wcroot,
14028 const char *local_relpath,
14029 apr_pool_t *scratch_pool)
14031 svn_sqlite__stmt_t *stmt;
14033 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_WC_LOCK));
14034 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14036 SVN_ERR(svn_sqlite__step_done(stmt));
14038 return SVN_NO_ERROR;
14042 /* The body of svn_wc__db_wclock_obtain().
14044 static svn_error_t *
14045 wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot,
14046 const char *local_relpath,
14047 int levels_to_lock,
14048 svn_boolean_t steal_lock,
14049 apr_pool_t *scratch_pool)
14051 svn_sqlite__stmt_t *stmt;
14053 const char *lock_relpath;
14056 svn_boolean_t got_row;
14058 svn_wc__db_wclock_t lock;
14060 /* Upgrade locks the root before the node exists. Apart from that
14061 the root node always exists so we will just skip the check.
14063 ### Perhaps the lock for upgrade should be created when the db is
14064 created? 1.6 used to lock .svn on creation. */
14065 if (local_relpath[0])
14067 svn_boolean_t exists;
14069 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
14071 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14072 _("The node '%s' was not found."),
14073 path_for_error_message(wcroot,
14078 /* Check if there are nodes locked below the new lock root */
14079 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_FIND_WC_LOCK));
14080 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14082 lock_depth = relpath_depth(local_relpath);
14083 max_depth = lock_depth + levels_to_lock;
14085 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14089 svn_boolean_t own_lock;
14091 lock_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
14093 /* If we are not locking with depth infinity, check if this lock
14094 voids our lock request */
14095 if (levels_to_lock >= 0
14096 && relpath_depth(lock_relpath) > max_depth)
14098 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14102 /* Check if we are the lock owner, because we should be able to
14103 extend our lock. */
14104 err = wclock_owns_lock(&own_lock, wcroot, lock_relpath,
14105 TRUE, scratch_pool);
14108 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14110 if (!own_lock && !steal_lock)
14112 SVN_ERR(svn_sqlite__reset(stmt));
14113 err = svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
14114 _("'%s' is already locked."),
14115 path_for_error_message(wcroot,
14118 return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14119 _("Working copy '%s' locked."),
14120 path_for_error_message(wcroot,
14124 else if (!own_lock)
14126 err = wclock_steal(wcroot, lock_relpath, scratch_pool);
14129 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14132 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14135 SVN_ERR(svn_sqlite__reset(stmt));
14138 SVN_ERR(wclock_steal(wcroot, local_relpath, scratch_pool));
14140 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_WC_LOCK));
14141 lock_relpath = local_relpath;
14145 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, lock_relpath));
14147 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14151 int levels = svn_sqlite__column_int(stmt, 0);
14153 levels += relpath_depth(lock_relpath);
14155 SVN_ERR(svn_sqlite__reset(stmt));
14157 if (levels == -1 || levels >= lock_depth)
14160 err = svn_error_createf(
14161 SVN_ERR_WC_LOCKED, NULL,
14162 _("'%s' is already locked."),
14163 svn_dirent_local_style(
14164 svn_dirent_join(wcroot->abspath,
14168 return svn_error_createf(
14169 SVN_ERR_WC_LOCKED, err,
14170 _("Working copy '%s' locked."),
14171 path_for_error_message(wcroot,
14176 break; /* There can't be interesting locks on higher nodes */
14179 SVN_ERR(svn_sqlite__reset(stmt));
14181 if (!*lock_relpath)
14184 lock_relpath = svn_relpath_dirname(lock_relpath, scratch_pool);
14187 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_WC_LOCK));
14188 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14190 err = svn_sqlite__insert(NULL, stmt);
14192 return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14193 _("Working copy '%s' locked"),
14194 path_for_error_message(wcroot,
14198 /* And finally store that we obtained the lock */
14199 lock.local_relpath = apr_pstrdup(wcroot->owned_locks->pool, local_relpath);
14200 lock.levels = levels_to_lock;
14201 APR_ARRAY_PUSH(wcroot->owned_locks, svn_wc__db_wclock_t) = lock;
14203 return SVN_NO_ERROR;
14208 svn_wc__db_wclock_obtain(svn_wc__db_t *db,
14209 const char *local_abspath,
14210 int levels_to_lock,
14211 svn_boolean_t steal_lock,
14212 apr_pool_t *scratch_pool)
14214 svn_wc__db_wcroot_t *wcroot;
14215 const char *local_relpath;
14217 SVN_ERR_ASSERT(levels_to_lock >= -1);
14218 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14220 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14222 scratch_pool, scratch_pool));
14223 VERIFY_USABLE_WCROOT(wcroot);
14228 int depth = relpath_depth(local_relpath);
14230 for (i = 0; i < wcroot->owned_locks->nelts; i++)
14232 svn_wc__db_wclock_t* lock = &APR_ARRAY_IDX(wcroot->owned_locks,
14233 i, svn_wc__db_wclock_t);
14235 if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14236 && (lock->levels == -1
14237 || (lock->levels + relpath_depth(lock->local_relpath))
14240 return svn_error_createf(
14241 SVN_ERR_WC_LOCKED, NULL,
14242 _("'%s' is already locked via '%s'."),
14243 svn_dirent_local_style(local_abspath, scratch_pool),
14244 path_for_error_message(wcroot, lock->local_relpath,
14250 SVN_WC__DB_WITH_TXN(
14251 wclock_obtain_cb(wcroot, local_relpath, levels_to_lock, steal_lock,
14254 return SVN_NO_ERROR;
14258 /* The body of svn_wc__db_wclock_find_root() and svn_wc__db_wclocked(). */
14259 static svn_error_t *
14260 find_wclock(const char **lock_relpath,
14261 svn_wc__db_wcroot_t *wcroot,
14262 const char *dir_relpath,
14263 apr_pool_t *result_pool,
14264 apr_pool_t *scratch_pool)
14266 svn_sqlite__stmt_t *stmt;
14267 svn_boolean_t have_row;
14268 int dir_depth = relpath_depth(dir_relpath);
14269 const char *first_relpath;
14271 /* Check for locks on all directories that might be ancestors.
14272 As our new apis only use recursive locks the number of locks stored
14273 in the DB will be very low */
14274 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14275 STMT_SELECT_ANCESTOR_WCLOCKS));
14277 /* Get the top level relpath to reduce the worst case number of results
14278 to the number of directories below this node plus two.
14279 (1: the node itself and 2: the wcroot). */
14280 first_relpath = strchr(dir_relpath, '/');
14282 if (first_relpath != NULL)
14283 first_relpath = apr_pstrndup(scratch_pool, dir_relpath,
14284 first_relpath - dir_relpath);
14286 first_relpath = dir_relpath;
14288 SVN_ERR(svn_sqlite__bindf(stmt, "iss",
14293 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14297 const char *relpath = svn_sqlite__column_text(stmt, 0, NULL);
14299 if (svn_relpath_skip_ancestor(relpath, dir_relpath))
14301 int locked_levels = svn_sqlite__column_int(stmt, 1);
14302 int row_depth = relpath_depth(relpath);
14304 if (locked_levels == -1
14305 || locked_levels + row_depth >= dir_depth)
14307 *lock_relpath = apr_pstrdup(result_pool, relpath);
14308 SVN_ERR(svn_sqlite__reset(stmt));
14309 return SVN_NO_ERROR;
14313 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14316 *lock_relpath = NULL;
14318 return svn_error_trace(svn_sqlite__reset(stmt));
14321 static svn_error_t *
14322 is_wclocked(svn_boolean_t *locked,
14323 svn_wc__db_wcroot_t *wcroot,
14324 const char *dir_relpath,
14325 apr_pool_t *scratch_pool)
14327 const char *lock_relpath;
14329 SVN_ERR(find_wclock(&lock_relpath, wcroot, dir_relpath,
14330 scratch_pool, scratch_pool));
14331 *locked = (lock_relpath != NULL);
14332 return SVN_NO_ERROR;
14337 svn_wc__db_wclock_find_root(const char **lock_abspath,
14339 const char *local_abspath,
14340 apr_pool_t *result_pool,
14341 apr_pool_t *scratch_pool)
14343 svn_wc__db_wcroot_t *wcroot;
14344 const char *local_relpath;
14345 const char *lock_relpath;
14347 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14348 local_abspath, scratch_pool, scratch_pool));
14349 VERIFY_USABLE_WCROOT(wcroot);
14351 SVN_WC__DB_WITH_TXN(
14352 find_wclock(&lock_relpath, wcroot, local_relpath,
14353 scratch_pool, scratch_pool),
14357 *lock_abspath = NULL;
14359 SVN_ERR(svn_wc__db_from_relpath(lock_abspath, db, wcroot->abspath,
14360 lock_relpath, result_pool, scratch_pool));
14361 return SVN_NO_ERROR;
14366 svn_wc__db_wclocked(svn_boolean_t *locked,
14368 const char *local_abspath,
14369 apr_pool_t *scratch_pool)
14371 svn_wc__db_wcroot_t *wcroot;
14372 const char *local_relpath;
14374 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14375 local_abspath, scratch_pool, scratch_pool));
14376 VERIFY_USABLE_WCROOT(wcroot);
14378 SVN_WC__DB_WITH_TXN(
14379 is_wclocked(locked, wcroot, local_relpath, scratch_pool),
14382 return SVN_NO_ERROR;
14387 svn_wc__db_wclock_release(svn_wc__db_t *db,
14388 const char *local_abspath,
14389 apr_pool_t *scratch_pool)
14391 svn_sqlite__stmt_t *stmt;
14392 svn_wc__db_wcroot_t *wcroot;
14393 const char *local_relpath;
14395 apr_array_header_t *owned_locks;
14397 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14398 local_abspath, scratch_pool, scratch_pool));
14400 VERIFY_USABLE_WCROOT(wcroot);
14402 /* First check and remove the owns-lock information as failure in
14403 removing the db record implies that we have to steal the lock later. */
14404 owned_locks = wcroot->owned_locks;
14405 for (i = 0; i < owned_locks->nelts; i++)
14407 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14408 svn_wc__db_wclock_t);
14410 if (strcmp(lock->local_relpath, local_relpath) == 0)
14414 if (i >= owned_locks->nelts)
14415 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
14416 _("Working copy not locked at '%s'."),
14417 svn_dirent_local_style(local_abspath,
14420 if (i < owned_locks->nelts)
14422 owned_locks->nelts--;
14424 /* Move the last item in the array to the deleted place */
14425 if (owned_locks->nelts > 0)
14426 APR_ARRAY_IDX(owned_locks, i, svn_wc__db_wclock_t) =
14427 APR_ARRAY_IDX(owned_locks, owned_locks->nelts, svn_wc__db_wclock_t);
14430 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14431 STMT_DELETE_WC_LOCK));
14433 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14435 SVN_ERR(svn_sqlite__step_done(stmt));
14437 return SVN_NO_ERROR;
14441 /* Like svn_wc__db_wclock_owns_lock() but taking WCROOT+LOCAL_RELPATH instead
14442 of DB+LOCAL_ABSPATH. */
14443 static svn_error_t *
14444 wclock_owns_lock(svn_boolean_t *own_lock,
14445 svn_wc__db_wcroot_t *wcroot,
14446 const char *local_relpath,
14447 svn_boolean_t exact,
14448 apr_pool_t *scratch_pool)
14450 apr_array_header_t *owned_locks;
14455 owned_locks = wcroot->owned_locks;
14456 lock_level = relpath_depth(local_relpath);
14460 for (i = 0; i < owned_locks->nelts; i++)
14462 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14463 svn_wc__db_wclock_t);
14465 if (strcmp(lock->local_relpath, local_relpath) == 0)
14468 return SVN_NO_ERROR;
14474 for (i = 0; i < owned_locks->nelts; i++)
14476 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14477 svn_wc__db_wclock_t);
14479 if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14480 && (lock->levels == -1
14481 || ((relpath_depth(lock->local_relpath) + lock->levels)
14485 return SVN_NO_ERROR;
14490 return SVN_NO_ERROR;
14495 svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
14497 const char *local_abspath,
14498 svn_boolean_t exact,
14499 apr_pool_t *scratch_pool)
14501 svn_wc__db_wcroot_t *wcroot;
14502 const char *local_relpath;
14504 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14505 local_abspath, scratch_pool, scratch_pool));
14508 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
14509 _("The node '%s' was not found."),
14510 svn_dirent_local_style(local_abspath,
14513 VERIFY_USABLE_WCROOT(wcroot);
14515 SVN_ERR(wclock_owns_lock(own_lock, wcroot, local_relpath, exact,
14518 return SVN_NO_ERROR;
14521 /* The body of svn_wc__db_temp_op_end_directory_update().
14523 static svn_error_t *
14524 end_directory_update(svn_wc__db_wcroot_t *wcroot,
14525 const char *local_relpath,
14526 apr_pool_t *scratch_pool)
14528 svn_sqlite__stmt_t *stmt;
14529 svn_wc__db_status_t base_status;
14531 SVN_ERR(svn_wc__db_base_get_info_internal(&base_status, NULL, NULL, NULL,
14532 NULL, NULL, NULL, NULL, NULL,
14533 NULL, NULL, NULL, NULL, NULL, NULL,
14534 wcroot, local_relpath,
14535 scratch_pool, scratch_pool));
14537 if (base_status == svn_wc__db_status_normal)
14538 return SVN_NO_ERROR;
14540 SVN_ERR_ASSERT(base_status == svn_wc__db_status_incomplete);
14542 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14543 STMT_UPDATE_NODE_BASE_PRESENCE));
14544 SVN_ERR(svn_sqlite__bindf(stmt, "ist", wcroot->wc_id, local_relpath,
14545 presence_map, svn_wc__db_status_normal));
14546 SVN_ERR(svn_sqlite__step_done(stmt));
14548 return SVN_NO_ERROR;
14552 svn_wc__db_temp_op_end_directory_update(svn_wc__db_t *db,
14553 const char *local_dir_abspath,
14554 apr_pool_t *scratch_pool)
14556 svn_wc__db_wcroot_t *wcroot;
14557 const char *local_relpath;
14559 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
14561 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14562 local_dir_abspath, scratch_pool, scratch_pool));
14563 VERIFY_USABLE_WCROOT(wcroot);
14565 SVN_WC__DB_WITH_TXN(
14566 end_directory_update(wcroot, local_relpath, scratch_pool),
14569 SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_empty,
14572 return SVN_NO_ERROR;
14576 /* The body of svn_wc__db_temp_op_start_directory_update().
14578 static svn_error_t *
14579 start_directory_update_txn(svn_wc__db_wcroot_t *wcroot,
14580 const char *local_relpath,
14581 const char *new_repos_relpath,
14582 svn_revnum_t new_rev,
14583 apr_pool_t *scratch_pool)
14585 svn_sqlite__stmt_t *stmt;
14587 /* Note: In the majority of calls, the repos_relpath is unchanged. */
14588 /* ### TODO: Maybe check if we can make repos_relpath NULL. */
14589 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14590 STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH));
14592 SVN_ERR(svn_sqlite__bindf(stmt, "istrs",
14595 presence_map, svn_wc__db_status_incomplete,
14597 new_repos_relpath));
14598 SVN_ERR(svn_sqlite__step_done(stmt));
14600 return SVN_NO_ERROR;
14605 svn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db,
14606 const char *local_abspath,
14607 const char *new_repos_relpath,
14608 svn_revnum_t new_rev,
14609 apr_pool_t *scratch_pool)
14611 svn_wc__db_wcroot_t *wcroot;
14612 const char *local_relpath;
14614 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14615 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_rev));
14616 SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
14618 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14619 local_abspath, scratch_pool, scratch_pool));
14620 VERIFY_USABLE_WCROOT(wcroot);
14622 SVN_WC__DB_WITH_TXN(
14623 start_directory_update_txn(wcroot, local_relpath,
14624 new_repos_relpath, new_rev, scratch_pool),
14627 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
14629 return SVN_NO_ERROR;
14633 /* The body of svn_wc__db_temp_op_make_copy(). This is
14634 used by the update editor when deleting a base node tree would be a
14635 tree-conflict because there are changes to subtrees. This function
14636 inserts a copy of the base node tree below any existing working
14637 subtrees. Given a tree:
14642 A/B normal - normal
14643 A/B/C normal - base-del normal
14644 A/F normal - normal
14645 A/F/G normal - normal
14646 A/F/H normal - base-deleted normal
14647 A/F/E normal - not-present
14651 This function adds layers to A and some of its descendants in an attempt
14652 to make the working copy look like as if it were a copy of the BASE nodes.
14657 A/B normal norm norm
14658 A/B/C normal norm base-del normal
14659 A/F normal norm norm
14660 A/F/G normal norm norm
14661 A/F/H normal norm not-pres
14662 A/F/E normal norm base-del
14664 A/X/Y incomplete incomplete
14666 static svn_error_t *
14667 make_copy_txn(svn_wc__db_wcroot_t *wcroot,
14668 const char *local_relpath,
14670 const svn_skel_t *conflicts,
14671 const svn_skel_t *work_items,
14672 apr_pool_t *scratch_pool)
14674 svn_sqlite__stmt_t *stmt;
14675 svn_boolean_t have_row;
14676 svn_boolean_t add_working_base_deleted = FALSE;
14677 svn_boolean_t remove_working = FALSE;
14678 const apr_array_header_t *children;
14679 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
14682 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14683 STMT_SELECT_LOWEST_WORKING_NODE));
14684 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
14685 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14689 svn_wc__db_status_t working_status;
14690 int working_op_depth;
14692 working_status = svn_sqlite__column_token(stmt, 1, presence_map);
14693 working_op_depth = svn_sqlite__column_int(stmt, 0);
14694 SVN_ERR(svn_sqlite__reset(stmt));
14696 SVN_ERR_ASSERT(working_status == svn_wc__db_status_normal
14697 || working_status == svn_wc__db_status_base_deleted
14698 || working_status == svn_wc__db_status_not_present
14699 || working_status == svn_wc__db_status_incomplete);
14701 /* Only change nodes in the layers where we are creating the copy.
14702 Deletes in higher layers will just apply to the copy */
14703 if (working_op_depth <= op_depth)
14705 add_working_base_deleted = TRUE;
14707 if (working_status == svn_wc__db_status_base_deleted)
14708 remove_working = TRUE;
14712 SVN_ERR(svn_sqlite__reset(stmt));
14714 if (remove_working)
14716 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14717 STMT_DELETE_LOWEST_WORKING_NODE));
14718 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14719 SVN_ERR(svn_sqlite__step_done(stmt));
14722 if (add_working_base_deleted)
14724 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14725 STMT_INSERT_DELETE_FROM_BASE));
14726 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14728 SVN_ERR(svn_sqlite__step_done(stmt));
14732 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14733 STMT_INSERT_WORKING_NODE_FROM_BASE_COPY));
14734 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14736 SVN_ERR(svn_sqlite__step_done(stmt));
14739 /* Get the BASE children, as WORKING children don't need modifications */
14740 SVN_ERR(gather_repo_children(&children, wcroot, local_relpath,
14741 0, scratch_pool, iterpool));
14743 for (i = 0; i < children->nelts; i++)
14745 const char *name = APR_ARRAY_IDX(children, i, const char *);
14746 const char *copy_relpath;
14748 svn_pool_clear(iterpool);
14750 copy_relpath = svn_relpath_join(local_relpath, name, iterpool);
14752 SVN_ERR(make_copy_txn(wcroot, copy_relpath, op_depth, NULL, NULL,
14756 SVN_ERR(flush_entries(wcroot, svn_dirent_join(wcroot->abspath, local_relpath,
14758 svn_depth_empty, iterpool));
14761 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
14762 conflicts, iterpool));
14764 SVN_ERR(add_work_items(wcroot->sdb, work_items, iterpool));
14766 svn_pool_destroy(iterpool);
14768 return SVN_NO_ERROR;
14773 svn_wc__db_op_make_copy(svn_wc__db_t *db,
14774 const char *local_abspath,
14775 const svn_skel_t *conflicts,
14776 const svn_skel_t *work_items,
14777 apr_pool_t *scratch_pool)
14779 svn_wc__db_wcroot_t *wcroot;
14780 const char *local_relpath;
14781 svn_sqlite__stmt_t *stmt;
14782 svn_boolean_t have_row;
14784 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14786 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14787 local_abspath, scratch_pool, scratch_pool));
14788 VERIFY_USABLE_WCROOT(wcroot);
14790 /* The update editor is supposed to call this function when there is
14791 no working node for LOCAL_ABSPATH. */
14792 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14793 STMT_SELECT_WORKING_NODE));
14794 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14795 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14796 SVN_ERR(svn_sqlite__reset(stmt));
14798 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
14799 _("Modification of '%s' already exists"),
14800 path_for_error_message(wcroot,
14804 /* We don't allow copies to contain server-excluded nodes;
14805 the update editor is going to have to bail out. */
14806 SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath, scratch_pool));
14808 SVN_WC__DB_WITH_TXN(
14809 make_copy_txn(wcroot, local_relpath,
14810 relpath_depth(local_relpath), conflicts, work_items,
14814 return SVN_NO_ERROR;
14818 svn_wc__db_info_below_working(svn_boolean_t *have_base,
14819 svn_boolean_t *have_work,
14820 svn_wc__db_status_t *status,
14822 const char *local_abspath,
14823 apr_pool_t *scratch_pool)
14825 svn_wc__db_wcroot_t *wcroot;
14826 const char *local_relpath;
14828 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14830 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14831 local_abspath, scratch_pool, scratch_pool));
14832 VERIFY_USABLE_WCROOT(wcroot);
14833 SVN_ERR(info_below_working(have_base, have_work, status,
14834 wcroot, local_relpath, -1, scratch_pool));
14836 return SVN_NO_ERROR;
14840 svn_wc__db_get_not_present_descendants(const apr_array_header_t **descendants,
14842 const char *local_abspath,
14843 apr_pool_t *result_pool,
14844 apr_pool_t *scratch_pool)
14846 svn_wc__db_wcroot_t *wcroot;
14847 const char *local_relpath;
14848 svn_sqlite__stmt_t *stmt;
14849 svn_boolean_t have_row;
14851 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14853 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14854 local_abspath, scratch_pool, scratch_pool));
14855 VERIFY_USABLE_WCROOT(wcroot);
14857 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14858 STMT_SELECT_NOT_PRESENT_DESCENDANTS));
14860 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
14863 relpath_depth(local_relpath)));
14865 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14869 apr_array_header_t *paths;
14871 paths = apr_array_make(result_pool, 4, sizeof(const char*));
14874 const char *found_relpath = svn_sqlite__column_text(stmt, 0, NULL);
14876 APR_ARRAY_PUSH(paths, const char *)
14877 = apr_pstrdup(result_pool, svn_relpath_skip_ancestor(
14878 local_relpath, found_relpath));
14880 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14883 *descendants = paths;
14886 *descendants = apr_array_make(result_pool, 0, sizeof(const char*));
14888 return svn_error_trace(svn_sqlite__reset(stmt));
14892 /* Like svn_wc__db_min_max_revisions(),
14893 * but accepts a WCROOT/LOCAL_RELPATH pair. */
14894 static svn_error_t *
14895 get_min_max_revisions(svn_revnum_t *min_revision,
14896 svn_revnum_t *max_revision,
14897 svn_wc__db_wcroot_t *wcroot,
14898 const char *local_relpath,
14899 svn_boolean_t committed,
14900 apr_pool_t *scratch_pool)
14902 svn_sqlite__stmt_t *stmt;
14903 svn_revnum_t min_rev, max_rev;
14905 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14906 STMT_SELECT_MIN_MAX_REVISIONS));
14907 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14908 SVN_ERR(svn_sqlite__step_row(stmt));
14912 min_rev = svn_sqlite__column_revnum(stmt, 2);
14913 max_rev = svn_sqlite__column_revnum(stmt, 3);
14917 min_rev = svn_sqlite__column_revnum(stmt, 0);
14918 max_rev = svn_sqlite__column_revnum(stmt, 1);
14921 /* The statement returns exactly one row. */
14922 SVN_ERR(svn_sqlite__reset(stmt));
14925 *min_revision = min_rev;
14927 *max_revision = max_rev;
14929 return SVN_NO_ERROR;
14934 svn_wc__db_min_max_revisions(svn_revnum_t *min_revision,
14935 svn_revnum_t *max_revision,
14937 const char *local_abspath,
14938 svn_boolean_t committed,
14939 apr_pool_t *scratch_pool)
14941 svn_wc__db_wcroot_t *wcroot;
14942 const char *local_relpath;
14944 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14946 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14948 scratch_pool, scratch_pool));
14949 VERIFY_USABLE_WCROOT(wcroot);
14951 return svn_error_trace(get_min_max_revisions(min_revision, max_revision,
14952 wcroot, local_relpath,
14953 committed, scratch_pool));
14957 /* Set *IS_SPARSE_CHECKOUT TRUE if LOCAL_RELPATH or any of the nodes
14958 * within LOCAL_RELPATH is sparse, FALSE otherwise. */
14959 static svn_error_t *
14960 is_sparse_checkout_internal(svn_boolean_t *is_sparse_checkout,
14961 svn_wc__db_wcroot_t *wcroot,
14962 const char *local_relpath,
14963 apr_pool_t *scratch_pool)
14965 svn_sqlite__stmt_t *stmt;
14966 svn_boolean_t have_row;
14968 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14969 STMT_HAS_SPARSE_NODES));
14970 SVN_ERR(svn_sqlite__bindf(stmt, "is",
14973 /* If this query returns a row, the working copy is sparse. */
14974 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14975 *is_sparse_checkout = have_row;
14976 SVN_ERR(svn_sqlite__reset(stmt));
14978 return SVN_NO_ERROR;
14982 /* Like svn_wc__db_has_switched_subtrees(),
14983 * but accepts a WCROOT/LOCAL_RELPATH pair. */
14984 static svn_error_t *
14985 has_switched_subtrees(svn_boolean_t *is_switched,
14986 svn_wc__db_wcroot_t *wcroot,
14987 const char *local_relpath,
14988 const char *trail_url,
14989 apr_pool_t *scratch_pool)
14991 svn_sqlite__stmt_t *stmt;
14992 svn_boolean_t have_row;
14993 apr_int64_t repos_id;
14994 const char *repos_relpath;
14996 /* Optional argument handling for caller */
14998 return SVN_NO_ERROR;
15000 *is_switched = FALSE;
15002 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
15003 &repos_relpath, &repos_id,
15004 NULL, NULL, NULL, NULL, NULL,
15005 NULL, NULL, NULL, NULL, NULL,
15006 wcroot, local_relpath,
15007 scratch_pool, scratch_pool));
15009 /* First do the cheap check where we only need info on the origin itself */
15010 if (trail_url != NULL)
15012 const char *repos_root_url;
15014 apr_size_t len1, len2;
15016 /* If the trailing part of the URL of the working copy directory
15017 does not match the given trailing URL then the whole working
15018 copy is switched. */
15020 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot->sdb,
15021 repos_id, scratch_pool));
15022 url = svn_path_url_add_component2(repos_root_url, repos_relpath,
15025 len1 = strlen(trail_url);
15026 len2 = strlen(url);
15027 if ((len1 > len2) || strcmp(url + len2 - len1, trail_url))
15029 *is_switched = TRUE;
15030 return SVN_NO_ERROR;
15034 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_HAS_SWITCHED));
15035 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath, repos_relpath));
15036 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15038 *is_switched = TRUE;
15039 SVN_ERR(svn_sqlite__reset(stmt));
15041 return SVN_NO_ERROR;
15046 svn_wc__db_has_switched_subtrees(svn_boolean_t *is_switched,
15048 const char *local_abspath,
15049 const char *trail_url,
15050 apr_pool_t *scratch_pool)
15052 svn_wc__db_wcroot_t *wcroot;
15053 const char *local_relpath;
15055 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15057 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15059 scratch_pool, scratch_pool));
15060 VERIFY_USABLE_WCROOT(wcroot);
15062 return svn_error_trace(has_switched_subtrees(is_switched, wcroot,
15063 local_relpath, trail_url,
15068 svn_wc__db_get_excluded_subtrees(apr_hash_t **excluded_subtrees,
15070 const char *local_abspath,
15071 apr_pool_t *result_pool,
15072 apr_pool_t *scratch_pool)
15074 svn_wc__db_wcroot_t *wcroot;
15075 const char *local_relpath;
15076 svn_sqlite__stmt_t *stmt;
15077 svn_boolean_t have_row;
15079 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15080 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15082 scratch_pool, scratch_pool));
15083 VERIFY_USABLE_WCROOT(wcroot);
15085 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15086 STMT_SELECT_ALL_EXCLUDED_DESCENDANTS));
15087 SVN_ERR(svn_sqlite__bindf(stmt, "is",
15090 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15093 *excluded_subtrees = apr_hash_make(result_pool);
15095 *excluded_subtrees = NULL;
15099 const char *abs_path =
15100 svn_dirent_join(wcroot->abspath,
15101 svn_sqlite__column_text(stmt, 0, NULL),
15103 svn_hash_sets(*excluded_subtrees, abs_path, abs_path);
15104 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15107 SVN_ERR(svn_sqlite__reset(stmt));
15108 return SVN_NO_ERROR;
15111 /* Like svn_wc__db_has_local_mods(),
15112 * but accepts a WCROOT/LOCAL_RELPATH pair.
15113 * ### This needs a DB as well as a WCROOT/RELPATH pair... */
15114 static svn_error_t *
15115 has_local_mods(svn_boolean_t *is_modified,
15116 svn_wc__db_wcroot_t *wcroot,
15117 const char *local_relpath,
15119 svn_cancel_func_t cancel_func,
15120 void *cancel_baton,
15121 apr_pool_t *scratch_pool)
15123 svn_sqlite__stmt_t *stmt;
15125 /* Check for additions or deletions. */
15126 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15127 STMT_SUBTREE_HAS_TREE_MODIFICATIONS));
15128 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15129 /* If this query returns a row, the working copy is modified. */
15130 SVN_ERR(svn_sqlite__step(is_modified, stmt));
15131 SVN_ERR(svn_sqlite__reset(stmt));
15134 SVN_ERR(cancel_func(cancel_baton));
15136 if (! *is_modified)
15138 /* Check for property modifications. */
15139 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15140 STMT_SUBTREE_HAS_PROP_MODIFICATIONS));
15141 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15142 /* If this query returns a row, the working copy is modified. */
15143 SVN_ERR(svn_sqlite__step(is_modified, stmt));
15144 SVN_ERR(svn_sqlite__reset(stmt));
15147 SVN_ERR(cancel_func(cancel_baton));
15150 if (! *is_modified)
15152 apr_pool_t *iterpool = NULL;
15153 svn_boolean_t have_row;
15155 /* Check for text modifications. */
15156 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15157 STMT_SELECT_BASE_FILES_RECURSIVE));
15158 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15159 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15161 iterpool = svn_pool_create(scratch_pool);
15164 const char *node_abspath;
15165 svn_filesize_t recorded_size;
15166 apr_time_t recorded_time;
15167 svn_boolean_t skip_check = FALSE;
15172 err = cancel_func(cancel_baton);
15174 return svn_error_trace(svn_error_compose_create(
15176 svn_sqlite__reset(stmt)));
15179 svn_pool_clear(iterpool);
15181 node_abspath = svn_dirent_join(wcroot->abspath,
15182 svn_sqlite__column_text(stmt, 0,
15186 recorded_size = get_recorded_size(stmt, 1);
15187 recorded_time = svn_sqlite__column_int64(stmt, 2);
15189 if (recorded_size != SVN_INVALID_FILESIZE
15190 && recorded_time != 0)
15192 const svn_io_dirent2_t *dirent;
15194 err = svn_io_stat_dirent2(&dirent, node_abspath, FALSE, TRUE,
15195 iterpool, iterpool);
15197 return svn_error_trace(svn_error_compose_create(
15199 svn_sqlite__reset(stmt)));
15201 if (dirent->kind != svn_node_file)
15203 *is_modified = TRUE; /* Missing or obstruction */
15206 else if (dirent->filesize == recorded_size
15207 && dirent->mtime == recorded_time)
15209 /* The file is not modified */
15216 err = svn_wc__internal_file_modified_p(is_modified,
15221 return svn_error_trace(svn_error_compose_create(
15223 svn_sqlite__reset(stmt)));
15229 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15232 svn_pool_destroy(iterpool);
15234 SVN_ERR(svn_sqlite__reset(stmt));
15237 return SVN_NO_ERROR;
15242 svn_wc__db_has_local_mods(svn_boolean_t *is_modified,
15244 const char *local_abspath,
15245 svn_cancel_func_t cancel_func,
15246 void *cancel_baton,
15247 apr_pool_t *scratch_pool)
15249 svn_wc__db_wcroot_t *wcroot;
15250 const char *local_relpath;
15252 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15254 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15256 scratch_pool, scratch_pool));
15257 VERIFY_USABLE_WCROOT(wcroot);
15259 return svn_error_trace(has_local_mods(is_modified, wcroot, local_relpath,
15260 db, cancel_func, cancel_baton,
15265 /* The body of svn_wc__db_revision_status().
15267 static svn_error_t *
15268 revision_status_txn(svn_revnum_t *min_revision,
15269 svn_revnum_t *max_revision,
15270 svn_boolean_t *is_sparse_checkout,
15271 svn_boolean_t *is_modified,
15272 svn_boolean_t *is_switched,
15273 svn_wc__db_wcroot_t *wcroot,
15274 const char *local_relpath,
15276 const char *trail_url,
15277 svn_boolean_t committed,
15278 svn_cancel_func_t cancel_func,
15279 void *cancel_baton,
15280 apr_pool_t *scratch_pool)
15283 svn_boolean_t exists;
15285 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
15289 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
15290 _("The node '%s' was not found."),
15291 path_for_error_message(wcroot, local_relpath,
15295 /* Determine mixed-revisionness. */
15296 SVN_ERR(get_min_max_revisions(min_revision, max_revision, wcroot,
15297 local_relpath, committed, scratch_pool));
15300 SVN_ERR(cancel_func(cancel_baton));
15302 /* Determine sparseness. */
15303 SVN_ERR(is_sparse_checkout_internal(is_sparse_checkout, wcroot,
15304 local_relpath, scratch_pool));
15307 SVN_ERR(cancel_func(cancel_baton));
15309 /* Check for switched nodes. */
15311 err = has_switched_subtrees(is_switched, wcroot, local_relpath,
15312 trail_url, scratch_pool);
15316 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
15317 return svn_error_trace(err);
15319 svn_error_clear(err); /* No Base node, but no fatal error */
15320 *is_switched = FALSE;
15325 SVN_ERR(cancel_func(cancel_baton));
15327 /* Check for local mods. */
15328 SVN_ERR(has_local_mods(is_modified, wcroot, local_relpath, db,
15329 cancel_func, cancel_baton, scratch_pool));
15331 return SVN_NO_ERROR;
15336 svn_wc__db_revision_status(svn_revnum_t *min_revision,
15337 svn_revnum_t *max_revision,
15338 svn_boolean_t *is_sparse_checkout,
15339 svn_boolean_t *is_modified,
15340 svn_boolean_t *is_switched,
15342 const char *local_abspath,
15343 const char *trail_url,
15344 svn_boolean_t committed,
15345 svn_cancel_func_t cancel_func,
15346 void *cancel_baton,
15347 apr_pool_t *scratch_pool)
15349 svn_wc__db_wcroot_t *wcroot;
15350 const char *local_relpath;
15352 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15354 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15356 scratch_pool, scratch_pool));
15357 VERIFY_USABLE_WCROOT(wcroot);
15359 SVN_WC__DB_WITH_TXN(
15360 revision_status_txn(min_revision, max_revision,
15361 is_sparse_checkout, is_modified, is_switched,
15362 wcroot, local_relpath, db,
15363 trail_url, committed, cancel_func, cancel_baton,
15366 return SVN_NO_ERROR;
15371 svn_wc__db_base_get_lock_tokens_recursive(apr_hash_t **lock_tokens,
15373 const char *local_abspath,
15374 apr_pool_t *result_pool,
15375 apr_pool_t *scratch_pool)
15377 svn_wc__db_wcroot_t *wcroot;
15378 const char *local_relpath;
15379 svn_sqlite__stmt_t *stmt;
15380 svn_boolean_t have_row;
15381 apr_int64_t last_repos_id = INVALID_REPOS_ID;
15382 const char *last_repos_root_url = NULL;
15384 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15386 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15388 scratch_pool, scratch_pool));
15389 VERIFY_USABLE_WCROOT(wcroot);
15391 *lock_tokens = apr_hash_make(result_pool);
15393 /* Fetch all the lock tokens in and under LOCAL_RELPATH. */
15394 SVN_ERR(svn_sqlite__get_statement(
15395 &stmt, wcroot->sdb,
15396 STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE));
15398 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15399 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15402 apr_int64_t child_repos_id = svn_sqlite__column_int64(stmt, 0);
15403 const char *child_relpath = svn_sqlite__column_text(stmt, 1, NULL);
15404 const char *lock_token = svn_sqlite__column_text(stmt, 2, result_pool);
15406 if (child_repos_id != last_repos_id)
15408 svn_error_t *err = svn_wc__db_fetch_repos_info(&last_repos_root_url,
15415 return svn_error_trace(
15416 svn_error_compose_create(err,
15417 svn_sqlite__reset(stmt)));
15420 last_repos_id = child_repos_id;
15423 SVN_ERR_ASSERT(last_repos_root_url != NULL);
15424 svn_hash_sets(*lock_tokens,
15425 svn_path_url_add_component2(last_repos_root_url,
15426 child_relpath, result_pool),
15429 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15431 return svn_sqlite__reset(stmt);
15435 /* If EXPRESSION is false, cause the caller to return an SVN_ERR_WC_CORRUPT
15436 * error, showing EXPRESSION and the caller's LOCAL_RELPATH in the message. */
15437 #define VERIFY(expression) \
15439 if (! (expression)) \
15440 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, \
15441 _("database inconsistency at local_relpath='%s' verifying " \
15442 "expression '%s'"), local_relpath, #expression); \
15446 /* Verify consistency of the metadata concerning WCROOT. This is intended
15447 * for use only during testing and debugging, so is not intended to be
15450 * This code is a complement to any verification that we can do in SQLite
15451 * triggers. See, for example, 'wc-checks.sql'.
15453 * Some more verification steps we might want to add are:
15455 * * on every ACTUAL row (except root): a NODES row exists at its parent path
15456 * * the op-depth root must always exist and every intermediate too
15458 static svn_error_t *
15459 verify_wcroot(svn_wc__db_wcroot_t *wcroot,
15460 apr_pool_t *scratch_pool)
15462 svn_sqlite__stmt_t *stmt;
15463 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15465 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15466 STMT_SELECT_ALL_NODES));
15467 SVN_ERR(svn_sqlite__bindf(stmt, "i", wcroot->wc_id));
15470 svn_boolean_t have_row;
15471 const char *local_relpath, *parent_relpath;
15474 svn_pool_clear(iterpool);
15476 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15480 op_depth = svn_sqlite__column_int(stmt, 0);
15481 local_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
15482 parent_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
15484 /* Verify parent_relpath is the parent path of local_relpath */
15485 VERIFY((parent_relpath == NULL)
15486 ? (local_relpath[0] == '\0')
15487 : (strcmp(svn_relpath_dirname(local_relpath, iterpool),
15488 parent_relpath) == 0));
15490 /* Verify op_depth <= the tree depth of local_relpath */
15491 VERIFY(op_depth <= relpath_depth(local_relpath));
15493 /* Verify parent_relpath refers to a row that exists */
15494 /* TODO: Verify there is a suitable parent row - e.g. has op_depth <=
15495 * the child's and a suitable presence */
15496 if (parent_relpath && svn_sqlite__column_is_null(stmt, 3))
15498 svn_sqlite__stmt_t *stmt2;
15499 svn_boolean_t have_a_parent_row;
15501 SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
15502 STMT_SELECT_NODE_INFO));
15503 SVN_ERR(svn_sqlite__bindf(stmt2, "is", wcroot->wc_id,
15505 SVN_ERR(svn_sqlite__step(&have_a_parent_row, stmt2));
15506 VERIFY(have_a_parent_row);
15507 SVN_ERR(svn_sqlite__reset(stmt2));
15510 svn_pool_destroy(iterpool);
15512 return svn_error_trace(svn_sqlite__reset(stmt));
15516 svn_wc__db_verify(svn_wc__db_t *db,
15517 const char *wri_abspath,
15518 apr_pool_t *scratch_pool)
15520 svn_wc__db_wcroot_t *wcroot;
15521 const char *local_relpath;
15523 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15525 scratch_pool, scratch_pool));
15526 VERIFY_USABLE_WCROOT(wcroot);
15528 SVN_ERR(verify_wcroot(wcroot, scratch_pool));
15529 return SVN_NO_ERROR;
15533 svn_wc__db_bump_format(int *result_format,
15534 svn_boolean_t *bumped_format,
15536 const char *wcroot_abspath,
15537 apr_pool_t *scratch_pool)
15539 svn_sqlite__db_t *sdb;
15544 *bumped_format = FALSE;
15546 /* Do not scan upwards for a working copy root here to prevent accidental
15547 * upgrades of any working copies the WCROOT might be nested in.
15548 * Just try to open a DB at the specified path instead. */
15549 err = svn_wc__db_util_open_db(&sdb, wcroot_abspath, SDB_FILE,
15550 svn_sqlite__mode_readwrite,
15551 TRUE, /* exclusive */
15552 NULL, /* my statements */
15553 scratch_pool, scratch_pool);
15557 apr_hash_t *entries;
15559 /* Could not open an sdb. Check for an entries file instead. */
15560 err2 = svn_wc__read_entries_old(&entries, wcroot_abspath,
15561 scratch_pool, scratch_pool);
15562 if (err2 || apr_hash_count(entries) == 0)
15563 return svn_error_createf(SVN_ERR_WC_INVALID_OP_ON_CWD,
15564 svn_error_compose_create(err, err2),
15565 _("Can't upgrade '%s' as it is not a working copy root"),
15566 svn_dirent_local_style(wcroot_abspath, scratch_pool));
15568 /* An entries file was found. This is a pre-wc-ng working copy
15569 * so suggest an upgrade. */
15570 return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, err,
15571 _("Working copy '%s' is too old and must be upgraded to "
15572 "at least format %d, as created by Subversion %s"),
15573 svn_dirent_local_style(wcroot_abspath, scratch_pool),
15574 SVN_WC__WC_NG_VERSION,
15575 svn_wc__version_string_from_format(SVN_WC__WC_NG_VERSION));
15578 SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool));
15579 err = svn_wc__upgrade_sdb(result_format, wcroot_abspath,
15580 sdb, format, scratch_pool);
15582 if (err == SVN_NO_ERROR && bumped_format)
15583 *bumped_format = (*result_format > format);
15585 /* Make sure we return a different error than expected for upgrades from
15587 if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
15588 err = svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, err,
15589 _("Working copy upgrade failed"));
15591 err = svn_error_compose_create(err, svn_sqlite__close(sdb));
15593 return svn_error_trace(err);
15597 svn_wc__db_vacuum(svn_wc__db_t *db,
15598 const char *local_abspath,
15599 apr_pool_t *scratch_pool)
15601 svn_wc__db_wcroot_t *wcroot;
15602 const char *local_relpath;
15604 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15606 scratch_pool, scratch_pool));
15607 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_VACUUM));
15609 return SVN_NO_ERROR;