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;
712 /* The directory at the WCROOT has a NULL parent_relpath. Otherwise,
713 bind the appropriate parent_relpath. */
714 const char *parent_relpath =
715 (*local_relpath == '\0') ? NULL
716 : svn_relpath_dirname(local_relpath, scratch_pool);
718 if (pibb->repos_id == INVALID_REPOS_ID)
719 SVN_ERR(create_repos_id(&repos_id, pibb->repos_root_url, pibb->repos_uuid,
720 wcroot->sdb, scratch_pool));
722 SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
723 SVN_ERR_ASSERT(pibb->repos_relpath != NULL);
725 if (pibb->keep_recorded_info)
727 svn_boolean_t have_row;
728 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
729 STMT_SELECT_BASE_NODE));
730 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
731 SVN_ERR(svn_sqlite__step(&have_row, stmt));
734 /* Preserve size and modification time if caller asked us to. */
735 recorded_size = get_recorded_size(stmt, 6);
736 recorded_time = svn_sqlite__column_int64(stmt, 12);
738 SVN_ERR(svn_sqlite__reset(stmt));
741 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
742 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisr"
744 "isnnnnns", /* 12 - 19 */
745 wcroot->wc_id, /* 1 */
746 local_relpath, /* 2 */
747 0, /* op_depth is 0 for base */
748 parent_relpath, /* 4 */
752 presence_map, pibb->status, /* 8 */
753 (pibb->kind == svn_node_dir) ? /* 9 */
754 svn_token__to_word(depth_map, pibb->depth) : NULL,
755 kind_map, pibb->kind, /* 10 */
756 pibb->changed_rev, /* 11 */
757 pibb->changed_date, /* 12 */
758 pibb->changed_author, /* 13 */
759 (pibb->kind == svn_node_symlink) ?
760 pibb->target : NULL)); /* 19 */
761 if (pibb->kind == svn_node_file)
764 && pibb->status != svn_wc__db_status_not_present
765 && pibb->status != svn_wc__db_status_excluded
766 && pibb->status != svn_wc__db_status_server_excluded)
767 return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
768 _("The file '%s' has no checksum."),
769 path_for_error_message(wcroot, local_relpath,
772 SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, pibb->checksum,
775 if (recorded_size != SVN_INVALID_FILESIZE)
777 SVN_ERR(svn_sqlite__bind_int64(stmt, 16, recorded_size));
778 SVN_ERR(svn_sqlite__bind_int64(stmt, 17, recorded_time));
782 /* Set properties. Must be null if presence not normal or incomplete. */
783 assert(pibb->status == svn_wc__db_status_normal
784 || pibb->status == svn_wc__db_status_incomplete
785 || pibb->props == NULL);
786 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, pibb->props,
789 SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, pibb->iprops,
793 SVN_ERR(svn_sqlite__bind_properties(stmt, 18, pibb->dav_cache,
796 if (pibb->file_external)
797 SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
799 SVN_ERR(svn_sqlite__insert(NULL, stmt));
801 if (pibb->update_actual_props)
803 /* Cast away const, to allow calling property helpers */
804 apr_hash_t *base_props = (apr_hash_t *)pibb->props;
805 apr_hash_t *new_actual_props = (apr_hash_t *)pibb->new_actual_props;
807 if (base_props != NULL
808 && new_actual_props != NULL
809 && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
811 apr_array_header_t *diffs;
813 SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
816 if (diffs->nelts == 0)
817 new_actual_props = NULL;
820 SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props,
821 wcroot->sdb, scratch_pool));
824 if (pibb->kind == svn_node_dir && pibb->children)
825 SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
834 /* When this is not the root node, check shadowing behavior */
838 && ((pibb->status == svn_wc__db_status_normal)
839 || (pibb->status == svn_wc__db_status_incomplete))
840 && ! pibb->file_external)
842 SVN_ERR(svn_wc__db_extend_parent_delete(wcroot, local_relpath,
846 else if (pibb->status == svn_wc__db_status_not_present
847 || pibb->status == svn_wc__db_status_server_excluded
848 || pibb->status == svn_wc__db_status_excluded)
850 SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
855 if (pibb->delete_working)
857 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
858 STMT_DELETE_WORKING_NODE));
859 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
860 SVN_ERR(svn_sqlite__step_done(stmt));
862 if (pibb->insert_base_deleted)
864 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
865 STMT_INSERT_DELETE_FROM_BASE));
866 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
867 wcroot->wc_id, local_relpath,
868 relpath_depth(local_relpath)));
869 SVN_ERR(svn_sqlite__step_done(stmt));
872 SVN_ERR(add_work_items(wcroot->sdb, pibb->work_items, scratch_pool));
874 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
875 pibb->conflict, scratch_pool));
881 /* Initialize the baton with appropriate "blank" values. This allows the
882 insertion function to leave certain columns null. */
884 blank_iwb(insert_working_baton_t *piwb)
886 memset(piwb, 0, sizeof(*piwb));
887 piwb->changed_rev = SVN_INVALID_REVNUM;
888 piwb->depth = svn_depth_infinity;
890 /* ORIGINAL_REPOS_ID and ORIGINAL_REVNUM could use some kind of "nil"
891 value, but... meh. We'll avoid them if ORIGINAL_REPOS_RELPATH==NULL. */
895 /* Insert a row in NODES for each (const char *) child name in CHILDREN,
896 whose parent directory is LOCAL_RELPATH, at op_depth=OP_DEPTH. Set each
897 child's presence to 'incomplete', kind to 'unknown', repos_id to REPOS_ID,
898 repos_path by appending the child name to REPOS_PATH, and revision to
899 REVISION (which should match the parent's revision).
901 If REPOS_ID is INVALID_REPOS_ID, set each child's repos_id to null. */
903 insert_incomplete_children(svn_sqlite__db_t *sdb,
905 const char *local_relpath,
906 apr_int64_t repos_id,
907 const char *repos_path,
908 svn_revnum_t revision,
909 const apr_array_header_t *children,
911 apr_pool_t *scratch_pool)
913 svn_sqlite__stmt_t *stmt;
915 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
916 apr_hash_t *moved_to_relpaths = apr_hash_make(scratch_pool);
918 SVN_ERR_ASSERT(repos_path != NULL || op_depth > 0);
919 SVN_ERR_ASSERT((repos_id != INVALID_REPOS_ID)
920 == (repos_path != NULL));
922 /* If we're inserting WORKING nodes, we might be replacing existing
923 * nodes which were moved-away. We need to retain the moved-to relpath of
924 * such nodes in order not to lose move information during replace. */
927 for (i = children->nelts; i--; )
929 const char *name = APR_ARRAY_IDX(children, i, const char *);
930 svn_boolean_t have_row;
932 svn_pool_clear(iterpool);
934 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
935 STMT_SELECT_WORKING_NODE));
936 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id,
937 svn_relpath_join(local_relpath, name,
939 SVN_ERR(svn_sqlite__step(&have_row, stmt));
940 if (have_row && !svn_sqlite__column_is_null(stmt, 14))
941 svn_hash_sets(moved_to_relpaths, name,
942 svn_sqlite__column_text(stmt, 14, scratch_pool));
944 SVN_ERR(svn_sqlite__reset(stmt));
948 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
950 for (i = children->nelts; i--; )
952 const char *name = APR_ARRAY_IDX(children, i, const char *);
954 svn_pool_clear(iterpool);
956 SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnrsnsnnnnnnnnnnsn",
958 svn_relpath_join(local_relpath, name,
963 "incomplete", /* 8, presence */
964 "unknown", /* 10, kind */
966 svn_hash_gets(moved_to_relpaths, name)));
967 if (repos_id != INVALID_REPOS_ID)
969 SVN_ERR(svn_sqlite__bind_int64(stmt, 5, repos_id));
970 SVN_ERR(svn_sqlite__bind_text(stmt, 6,
971 svn_relpath_join(repos_path, name,
975 SVN_ERR(svn_sqlite__insert(NULL, stmt));
978 svn_pool_destroy(iterpool);
984 /* Insert the working row represented by (insert_working_baton_t *) BATON. */
986 insert_working_node(const insert_working_baton_t *piwb,
987 svn_wc__db_wcroot_t *wcroot,
988 const char *local_relpath,
989 apr_pool_t *scratch_pool)
991 const char *parent_relpath;
992 const char *moved_to_relpath = NULL;
993 svn_sqlite__stmt_t *stmt;
994 svn_boolean_t have_row;
996 SVN_ERR_ASSERT(piwb->op_depth > 0);
998 /* We cannot insert a WORKING_NODE row at the wcroot. */
999 SVN_ERR_ASSERT(*local_relpath != '\0');
1000 parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
1002 /* Preserve existing moved-to information for this relpath,
1003 * which might exist in case we're replacing an existing base-deleted
1005 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
1006 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1008 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1010 moved_to_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
1011 SVN_ERR(svn_sqlite__reset(stmt));
1013 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
1014 SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnntstrisn"
1015 "nnnn" /* properties translated_size last_mod_time dav_cache */
1016 "sns", /* symlink_target, file_external, moved_to */
1017 wcroot->wc_id, local_relpath,
1020 presence_map, piwb->presence,
1021 (piwb->kind == svn_node_dir)
1022 ? svn_token__to_word(depth_map, piwb->depth) : NULL,
1023 kind_map, piwb->kind,
1026 piwb->changed_author,
1027 /* Note: incomplete nodes may have a NULL target. */
1028 (piwb->kind == svn_node_symlink)
1029 ? piwb->target : NULL,
1032 if (piwb->moved_here)
1034 SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
1037 if (piwb->kind == svn_node_file)
1039 SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, piwb->checksum,
1043 if (piwb->original_repos_relpath != NULL)
1045 SVN_ERR(svn_sqlite__bind_int64(stmt, 5, piwb->original_repos_id));
1046 SVN_ERR(svn_sqlite__bind_text(stmt, 6, piwb->original_repos_relpath));
1047 SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, piwb->original_revnum));
1050 /* Set properties. Must be null if presence not normal or incomplete. */
1051 assert(piwb->presence == svn_wc__db_status_normal
1052 || piwb->presence == svn_wc__db_status_incomplete
1053 || piwb->props == NULL);
1054 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, piwb->props, scratch_pool));
1056 SVN_ERR(svn_sqlite__insert(NULL, stmt));
1058 /* Insert incomplete children, if specified.
1059 The children are part of the same op and so have the same op_depth.
1060 (The only time we'd want a different depth is during a recursive
1061 simple add, but we never insert children here during a simple add.) */
1062 if (piwb->kind == svn_node_dir && piwb->children)
1063 SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
1065 INVALID_REPOS_ID /* inherit repos_id */,
1066 NULL /* inherit repos_path */,
1067 piwb->original_revnum,
1072 if (piwb->update_actual_props)
1074 /* Cast away const, to allow calling property helpers */
1075 apr_hash_t *base_props = (apr_hash_t *)piwb->props;
1076 apr_hash_t *new_actual_props = (apr_hash_t *)piwb->new_actual_props;
1078 if (base_props != NULL
1079 && new_actual_props != NULL
1080 && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
1082 apr_array_header_t *diffs;
1084 SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
1087 if (diffs->nelts == 0)
1088 new_actual_props = NULL;
1091 SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props,
1092 wcroot->sdb, scratch_pool));
1095 if (piwb->kind == svn_node_dir)
1097 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1098 STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST));
1099 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1100 SVN_ERR(svn_sqlite__step_done(stmt));
1102 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1103 STMT_DELETE_ACTUAL_EMPTY));
1104 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1105 SVN_ERR(svn_sqlite__step_done(stmt));
1108 if (piwb->not_present_op_depth > 0
1109 && piwb->not_present_op_depth < piwb->op_depth)
1111 /* And also insert a not-present node to tell the commit processing that
1112 a child of the parent node was not copied. */
1113 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1116 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
1117 wcroot->wc_id, local_relpath,
1118 piwb->not_present_op_depth, parent_relpath,
1119 piwb->original_repos_id,
1120 piwb->original_repos_relpath,
1121 piwb->original_revnum,
1122 presence_map, svn_wc__db_status_not_present,
1124 kind_map, piwb->kind));
1126 SVN_ERR(svn_sqlite__step_done(stmt));
1129 SVN_ERR(add_work_items(wcroot->sdb, piwb->work_items, scratch_pool));
1131 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
1132 piwb->conflict, scratch_pool));
1134 return SVN_NO_ERROR;
1138 /* Each name is allocated in RESULT_POOL and stored into CHILDREN as a key
1139 pointed to the same name. */
1140 static svn_error_t *
1141 add_children_to_hash(apr_hash_t *children,
1143 svn_sqlite__db_t *sdb,
1145 const char *parent_relpath,
1146 apr_pool_t *result_pool)
1148 svn_sqlite__stmt_t *stmt;
1149 svn_boolean_t have_row;
1151 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, stmt_idx));
1152 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, parent_relpath));
1153 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1156 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1157 const char *name = svn_relpath_basename(child_relpath, result_pool);
1159 svn_hash_sets(children, name, name);
1161 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1164 return svn_sqlite__reset(stmt);
1168 /* Set *CHILDREN to a new array of the (const char *) basenames of the
1169 immediate children, whatever their status, of the working node at
1171 static svn_error_t *
1172 gather_children2(const apr_array_header_t **children,
1173 svn_wc__db_wcroot_t *wcroot,
1174 const char *local_relpath,
1175 apr_pool_t *result_pool,
1176 apr_pool_t *scratch_pool)
1178 apr_hash_t *names_hash = apr_hash_make(scratch_pool);
1179 apr_array_header_t *names_array;
1181 /* All of the names get allocated in RESULT_POOL. It
1182 appears to be faster to use the hash to remove duplicates than to
1183 use DISTINCT in the SQL query. */
1184 SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_WORKING_CHILDREN,
1185 wcroot->sdb, wcroot->wc_id,
1186 local_relpath, result_pool));
1188 SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool));
1189 *children = names_array;
1190 return SVN_NO_ERROR;
1193 /* Return in *CHILDREN all of the children of the directory LOCAL_RELPATH,
1194 of any status, in all op-depths in the NODES table. */
1195 static svn_error_t *
1196 gather_children(const apr_array_header_t **children,
1197 svn_wc__db_wcroot_t *wcroot,
1198 const char *local_relpath,
1199 apr_pool_t *result_pool,
1200 apr_pool_t *scratch_pool)
1202 apr_hash_t *names_hash = apr_hash_make(scratch_pool);
1203 apr_array_header_t *names_array;
1205 /* All of the names get allocated in RESULT_POOL. It
1206 appears to be faster to use the hash to remove duplicates than to
1207 use DISTINCT in the SQL query. */
1208 SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_NODE_CHILDREN,
1209 wcroot->sdb, wcroot->wc_id,
1210 local_relpath, result_pool));
1212 SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool));
1213 *children = names_array;
1214 return SVN_NO_ERROR;
1218 /* Set *CHILDREN to a new array of (const char *) names of the children of
1219 the repository directory corresponding to WCROOT:LOCAL_RELPATH:OP_DEPTH -
1220 that is, only the children that are at the same op-depth as their parent. */
1221 static svn_error_t *
1222 gather_repo_children(const apr_array_header_t **children,
1223 svn_wc__db_wcroot_t *wcroot,
1224 const char *local_relpath,
1226 apr_pool_t *result_pool,
1227 apr_pool_t *scratch_pool)
1229 apr_array_header_t *result
1230 = apr_array_make(result_pool, 0, sizeof(const char *));
1231 svn_sqlite__stmt_t *stmt;
1232 svn_boolean_t have_row;
1234 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1235 STMT_SELECT_OP_DEPTH_CHILDREN));
1236 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1238 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1241 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1243 /* Allocate the name in RESULT_POOL so we won't have to copy it. */
1244 APR_ARRAY_PUSH(result, const char *)
1245 = svn_relpath_basename(child_relpath, result_pool);
1247 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1249 SVN_ERR(svn_sqlite__reset(stmt));
1252 return SVN_NO_ERROR;
1256 svn_wc__db_get_children_op_depth(apr_hash_t **children,
1257 svn_wc__db_wcroot_t *wcroot,
1258 const char *local_relpath,
1260 apr_pool_t *result_pool,
1261 apr_pool_t *scratch_pool)
1263 svn_sqlite__stmt_t *stmt;
1264 svn_boolean_t have_row;
1266 *children = apr_hash_make(result_pool);
1268 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1269 STMT_SELECT_OP_DEPTH_CHILDREN));
1270 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1272 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1275 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1276 svn_node_kind_t *child_kind = apr_palloc(result_pool, sizeof(svn_node_kind_t));
1278 *child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
1279 svn_hash_sets(*children,
1280 svn_relpath_basename(child_relpath, result_pool),
1283 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1285 SVN_ERR(svn_sqlite__reset(stmt));
1287 return SVN_NO_ERROR;
1291 /* Return TRUE if CHILD_ABSPATH is an immediate child of PARENT_ABSPATH.
1292 * Else, return FALSE. */
1293 static svn_boolean_t
1294 is_immediate_child_path(const char *parent_abspath, const char *child_abspath)
1296 const char *local_relpath = svn_dirent_skip_ancestor(parent_abspath,
1299 /* To be an immediate child local_relpath should have one (not empty)
1301 return local_relpath && *local_relpath && !strchr(local_relpath, '/');
1305 /* Remove the access baton for LOCAL_ABSPATH from ACCESS_CACHE. */
1307 remove_from_access_cache(apr_hash_t *access_cache,
1308 const char *local_abspath)
1310 svn_wc_adm_access_t *adm_access;
1312 adm_access = svn_hash_gets(access_cache, local_abspath);
1314 svn_wc__adm_access_set_entries(adm_access, NULL);
1318 /* Flush the access baton for LOCAL_ABSPATH, and any of its children up to
1319 * the specified DEPTH, from the access baton cache in WCROOT.
1320 * Also flush the access baton for the parent of LOCAL_ABSPATH.I
1322 * This function must be called when the access baton cache goes stale,
1323 * i.e. data about LOCAL_ABSPATH will need to be read again from disk.
1325 * Use SCRATCH_POOL for temporary allocations. */
1326 static svn_error_t *
1327 flush_entries(svn_wc__db_wcroot_t *wcroot,
1328 const char *local_abspath,
1330 apr_pool_t *scratch_pool)
1332 const char *parent_abspath;
1334 if (apr_hash_count(wcroot->access_cache) == 0)
1335 return SVN_NO_ERROR;
1337 remove_from_access_cache(wcroot->access_cache, local_abspath);
1339 if (depth > svn_depth_empty)
1341 apr_hash_index_t *hi;
1343 /* Flush access batons of children within the specified depth. */
1344 for (hi = apr_hash_first(scratch_pool, wcroot->access_cache);
1346 hi = apr_hash_next(hi))
1348 const char *item_abspath = svn__apr_hash_index_key(hi);
1350 if ((depth == svn_depth_files || depth == svn_depth_immediates) &&
1351 is_immediate_child_path(local_abspath, item_abspath))
1353 remove_from_access_cache(wcroot->access_cache, item_abspath);
1355 else if (depth == svn_depth_infinity &&
1356 svn_dirent_is_ancestor(local_abspath, item_abspath))
1358 remove_from_access_cache(wcroot->access_cache, item_abspath);
1363 /* We're going to be overly aggressive here and just flush the parent
1364 without doing much checking. This may hurt performance for
1365 legacy API consumers, but that's not our problem. :) */
1366 parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
1367 remove_from_access_cache(wcroot->access_cache, parent_abspath);
1369 return SVN_NO_ERROR;
1373 /* Add a single WORK_ITEM into the given SDB's WORK_QUEUE table. This does
1374 not perform its work within a transaction, assuming the caller will
1376 static svn_error_t *
1377 add_single_work_item(svn_sqlite__db_t *sdb,
1378 const svn_skel_t *work_item,
1379 apr_pool_t *scratch_pool)
1381 svn_stringbuf_t *serialized;
1382 svn_sqlite__stmt_t *stmt;
1384 serialized = svn_skel__unparse(work_item, scratch_pool);
1385 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_WORK_ITEM));
1386 SVN_ERR(svn_sqlite__bind_blob(stmt, 1, serialized->data, serialized->len));
1387 return svn_error_trace(svn_sqlite__insert(NULL, stmt));
1391 /* Add work item(s) to the given SDB. Also see add_single_work_item(). This
1392 SKEL is usually passed to the various wc_db operation functions. It may
1393 be NULL, indicating no additional work items are needed, it may be a
1394 single work item, or it may be a list of work items. */
1395 static svn_error_t *
1396 add_work_items(svn_sqlite__db_t *sdb,
1397 const svn_skel_t *skel,
1398 apr_pool_t *scratch_pool)
1400 apr_pool_t *iterpool;
1402 /* Maybe there are no work items to insert. */
1404 return SVN_NO_ERROR;
1406 /* Should have a list. */
1407 SVN_ERR_ASSERT(!skel->is_atom);
1409 /* Is the list a single work item? Or a list of work items? */
1410 if (SVN_WC__SINGLE_WORK_ITEM(skel))
1411 return svn_error_trace(add_single_work_item(sdb, skel, scratch_pool));
1413 /* SKEL is a list-of-lists, aka list of work items. */
1415 iterpool = svn_pool_create(scratch_pool);
1416 for (skel = skel->children; skel; skel = skel->next)
1418 svn_pool_clear(iterpool);
1420 SVN_ERR(add_single_work_item(sdb, skel, iterpool));
1422 svn_pool_destroy(iterpool);
1424 return SVN_NO_ERROR;
1428 /* Determine whether the node exists for a given WCROOT and LOCAL_RELPATH. */
1429 static svn_error_t *
1430 does_node_exist(svn_boolean_t *exists,
1431 const svn_wc__db_wcroot_t *wcroot,
1432 const char *local_relpath)
1434 svn_sqlite__stmt_t *stmt;
1436 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DOES_NODE_EXIST));
1437 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1438 SVN_ERR(svn_sqlite__step(exists, stmt));
1440 return svn_error_trace(svn_sqlite__reset(stmt));
1444 svn_wc__db_install_schema_statistics(svn_sqlite__db_t *sdb,
1445 apr_pool_t *scratch_pool)
1447 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_INSTALL_SCHEMA_STATISTICS));
1449 return SVN_NO_ERROR;
1452 /* Helper for create_db(). Initializes our wc.db schema.
1454 static svn_error_t *
1455 init_db(/* output values */
1456 apr_int64_t *repos_id,
1459 svn_sqlite__db_t *db,
1460 const char *repos_root_url,
1461 const char *repos_uuid,
1462 const char *root_node_repos_relpath,
1463 svn_revnum_t root_node_revision,
1464 svn_depth_t root_node_depth,
1465 apr_pool_t *scratch_pool)
1467 svn_sqlite__stmt_t *stmt;
1469 /* Create the database's schema. */
1470 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_SCHEMA));
1471 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES));
1472 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES_TRIGGERS));
1473 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_EXTERNALS));
1475 /* Insert the repository. */
1476 SVN_ERR(create_repos_id(repos_id, repos_root_url, repos_uuid,
1479 SVN_ERR(svn_wc__db_install_schema_statistics(db, scratch_pool));
1481 /* Insert the wcroot. */
1482 /* ### Right now, this just assumes wc metadata is being stored locally. */
1483 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT));
1484 SVN_ERR(svn_sqlite__insert(wc_id, stmt));
1486 if (root_node_repos_relpath)
1488 svn_wc__db_status_t status = svn_wc__db_status_normal;
1490 if (root_node_revision > 0)
1491 status = svn_wc__db_status_incomplete; /* Will be filled by update */
1493 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_NODE));
1494 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtst",
1497 0, /* op_depth is 0 for base */
1500 root_node_repos_relpath,
1502 presence_map, status, /* 8 */
1503 svn_token__to_word(depth_map,
1505 kind_map, svn_node_dir /* 10 */));
1507 SVN_ERR(svn_sqlite__insert(NULL, stmt));
1510 return SVN_NO_ERROR;
1513 /* Create an sqlite database at DIR_ABSPATH/SDB_FNAME and insert
1514 records for REPOS_ID (using REPOS_ROOT_URL and REPOS_UUID) into
1515 REPOSITORY and for WC_ID into WCROOT. Return the DB connection
1518 If ROOT_NODE_REPOS_RELPATH is not NULL, insert a BASE node at
1519 the working copy root with repository relpath ROOT_NODE_REPOS_RELPATH,
1520 revision ROOT_NODE_REVISION and depth ROOT_NODE_DEPTH.
1522 static svn_error_t *
1523 create_db(svn_sqlite__db_t **sdb,
1524 apr_int64_t *repos_id,
1526 const char *dir_abspath,
1527 const char *repos_root_url,
1528 const char *repos_uuid,
1529 const char *sdb_fname,
1530 const char *root_node_repos_relpath,
1531 svn_revnum_t root_node_revision,
1532 svn_depth_t root_node_depth,
1533 svn_boolean_t exclusive,
1534 apr_pool_t *result_pool,
1535 apr_pool_t *scratch_pool)
1537 SVN_ERR(svn_wc__db_util_open_db(sdb, dir_abspath, sdb_fname,
1538 svn_sqlite__mode_rwcreate, exclusive,
1539 NULL /* my_statements */,
1540 result_pool, scratch_pool));
1542 SVN_SQLITE__WITH_LOCK(init_db(repos_id, wc_id,
1543 *sdb, repos_root_url, repos_uuid,
1544 root_node_repos_relpath, root_node_revision,
1545 root_node_depth, scratch_pool),
1548 return SVN_NO_ERROR;
1553 svn_wc__db_init(svn_wc__db_t *db,
1554 const char *local_abspath,
1555 const char *repos_relpath,
1556 const char *repos_root_url,
1557 const char *repos_uuid,
1558 svn_revnum_t initial_rev,
1560 apr_pool_t *scratch_pool)
1562 svn_sqlite__db_t *sdb;
1563 apr_int64_t repos_id;
1565 svn_wc__db_wcroot_t *wcroot;
1566 svn_boolean_t sqlite_exclusive = FALSE;
1568 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1569 SVN_ERR_ASSERT(repos_relpath != NULL);
1570 SVN_ERR_ASSERT(depth == svn_depth_empty
1571 || depth == svn_depth_files
1572 || depth == svn_depth_immediates
1573 || depth == svn_depth_infinity);
1575 /* ### REPOS_ROOT_URL and REPOS_UUID may be NULL. ... more doc: tbd */
1577 SVN_ERR(svn_config_get_bool((svn_config_t *)db->config, &sqlite_exclusive,
1578 SVN_CONFIG_SECTION_WORKING_COPY,
1579 SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
1582 /* Create the SDB and insert the basic rows. */
1583 SVN_ERR(create_db(&sdb, &repos_id, &wc_id, local_abspath, repos_root_url,
1584 repos_uuid, SDB_FILE,
1585 repos_relpath, initial_rev, depth, sqlite_exclusive,
1586 db->state_pool, scratch_pool));
1588 /* Create the WCROOT for this directory. */
1589 SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
1590 apr_pstrdup(db->state_pool, local_abspath),
1591 sdb, wc_id, FORMAT_FROM_SDB,
1592 FALSE /* auto-upgrade */,
1593 FALSE /* enforce_empty_wq */,
1594 db->state_pool, scratch_pool));
1596 /* The WCROOT is complete. Stash it into DB. */
1597 svn_hash_sets(db->dir_data, wcroot->abspath, wcroot);
1599 return SVN_NO_ERROR;
1604 svn_wc__db_to_relpath(const char **local_relpath,
1606 const char *wri_abspath,
1607 const char *local_abspath,
1608 apr_pool_t *result_pool,
1609 apr_pool_t *scratch_pool)
1611 svn_wc__db_wcroot_t *wcroot;
1612 const char *relpath;
1614 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1616 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &relpath, db,
1617 wri_abspath, result_pool, scratch_pool));
1619 /* This function is indirectly called from the upgrade code, so we
1620 can't verify the wcroot here. Just check that it is not NULL */
1621 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1623 if (svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
1625 *local_relpath = apr_pstrdup(result_pool,
1626 svn_dirent_skip_ancestor(wcroot->abspath,
1630 /* Probably moving from $TMP. Should we allow this? */
1631 *local_relpath = apr_pstrdup(result_pool, local_abspath);
1633 return SVN_NO_ERROR;
1638 svn_wc__db_from_relpath(const char **local_abspath,
1640 const char *wri_abspath,
1641 const char *local_relpath,
1642 apr_pool_t *result_pool,
1643 apr_pool_t *scratch_pool)
1645 svn_wc__db_wcroot_t *wcroot;
1646 const char *unused_relpath;
1648 SVN_ERR_ASSERT(svn_relpath_is_canonical(local_relpath));
1651 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1652 wri_abspath, scratch_pool, scratch_pool));
1654 /* This function is indirectly called from the upgrade code, so we
1655 can't verify the wcroot here. Just check that it is not NULL */
1656 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1659 *local_abspath = svn_dirent_join(wcroot->abspath,
1662 return SVN_NO_ERROR;
1667 svn_wc__db_get_wcroot(const char **wcroot_abspath,
1669 const char *wri_abspath,
1670 apr_pool_t *result_pool,
1671 apr_pool_t *scratch_pool)
1673 svn_wc__db_wcroot_t *wcroot;
1674 const char *unused_relpath;
1676 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1677 wri_abspath, scratch_pool, scratch_pool));
1679 /* Can't use VERIFY_USABLE_WCROOT, as this should be usable to detect
1680 where call upgrade */
1681 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1683 *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
1685 return SVN_NO_ERROR;
1690 svn_wc__db_base_add_directory(svn_wc__db_t *db,
1691 const char *local_abspath,
1692 const char *wri_abspath,
1693 const char *repos_relpath,
1694 const char *repos_root_url,
1695 const char *repos_uuid,
1696 svn_revnum_t revision,
1697 const apr_hash_t *props,
1698 svn_revnum_t changed_rev,
1699 apr_time_t changed_date,
1700 const char *changed_author,
1701 const apr_array_header_t *children,
1703 apr_hash_t *dav_cache,
1704 const svn_skel_t *conflict,
1705 svn_boolean_t update_actual_props,
1706 apr_hash_t *new_actual_props,
1707 apr_array_header_t *new_iprops,
1708 const svn_skel_t *work_items,
1709 apr_pool_t *scratch_pool)
1711 svn_wc__db_wcroot_t *wcroot;
1712 const char *local_relpath;
1713 insert_base_baton_t ibb;
1715 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1716 SVN_ERR_ASSERT(repos_relpath != NULL);
1717 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1718 SVN_ERR_ASSERT(repos_uuid != NULL);
1719 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1720 SVN_ERR_ASSERT(props != NULL);
1721 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1723 SVN_ERR_ASSERT(children != NULL);
1726 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1727 wri_abspath, scratch_pool, scratch_pool));
1728 VERIFY_USABLE_WCROOT(wcroot);
1729 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1733 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1734 ibb.repos_root_url = repos_root_url;
1735 ibb.repos_uuid = repos_uuid;
1737 ibb.status = svn_wc__db_status_normal;
1738 ibb.kind = svn_node_dir;
1739 ibb.repos_relpath = repos_relpath;
1740 ibb.revision = revision;
1742 ibb.iprops = new_iprops;
1744 ibb.changed_rev = changed_rev;
1745 ibb.changed_date = changed_date;
1746 ibb.changed_author = changed_author;
1748 ibb.children = children;
1751 ibb.dav_cache = dav_cache;
1752 ibb.conflict = conflict;
1753 ibb.work_items = work_items;
1755 if (update_actual_props)
1757 ibb.update_actual_props = TRUE;
1758 ibb.new_actual_props = new_actual_props;
1761 /* Insert the directory and all its children transactionally.
1763 Note: old children can stick around, even if they are no longer present
1764 in this directory's revision. */
1765 SVN_WC__DB_WITH_TXN(
1766 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1769 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
1770 return SVN_NO_ERROR;
1774 svn_wc__db_base_add_incomplete_directory(svn_wc__db_t *db,
1775 const char *local_abspath,
1776 const char *repos_relpath,
1777 const char *repos_root_url,
1778 const char *repos_uuid,
1779 svn_revnum_t revision,
1781 svn_boolean_t insert_base_deleted,
1782 svn_boolean_t delete_working,
1783 svn_skel_t *conflict,
1784 svn_skel_t *work_items,
1785 apr_pool_t *scratch_pool)
1787 svn_wc__db_wcroot_t *wcroot;
1788 const char *local_relpath;
1789 struct insert_base_baton_t ibb;
1791 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1792 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1793 SVN_ERR_ASSERT(repos_relpath && repos_root_url && repos_uuid);
1795 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
1797 scratch_pool, scratch_pool));
1799 VERIFY_USABLE_WCROOT(wcroot);
1803 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1804 ibb.repos_root_url = repos_root_url;
1805 ibb.repos_uuid = repos_uuid;
1807 ibb.status = svn_wc__db_status_incomplete;
1808 ibb.kind = svn_node_dir;
1809 ibb.repos_relpath = repos_relpath;
1810 ibb.revision = revision;
1812 ibb.insert_base_deleted = insert_base_deleted;
1813 ibb.delete_working = delete_working;
1815 ibb.conflict = conflict;
1816 ibb.work_items = work_items;
1818 SVN_WC__DB_WITH_TXN(
1819 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1822 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
1824 return SVN_NO_ERROR;
1829 svn_wc__db_base_add_file(svn_wc__db_t *db,
1830 const char *local_abspath,
1831 const char *wri_abspath,
1832 const char *repos_relpath,
1833 const char *repos_root_url,
1834 const char *repos_uuid,
1835 svn_revnum_t revision,
1836 const apr_hash_t *props,
1837 svn_revnum_t changed_rev,
1838 apr_time_t changed_date,
1839 const char *changed_author,
1840 const svn_checksum_t *checksum,
1841 apr_hash_t *dav_cache,
1842 svn_boolean_t delete_working,
1843 svn_boolean_t update_actual_props,
1844 apr_hash_t *new_actual_props,
1845 apr_array_header_t *new_iprops,
1846 svn_boolean_t keep_recorded_info,
1847 svn_boolean_t insert_base_deleted,
1848 const svn_skel_t *conflict,
1849 const svn_skel_t *work_items,
1850 apr_pool_t *scratch_pool)
1852 svn_wc__db_wcroot_t *wcroot;
1853 const char *local_relpath;
1854 insert_base_baton_t ibb;
1856 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1857 SVN_ERR_ASSERT(repos_relpath != NULL);
1858 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1859 SVN_ERR_ASSERT(repos_uuid != NULL);
1860 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1861 SVN_ERR_ASSERT(props != NULL);
1862 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1863 SVN_ERR_ASSERT(checksum != NULL);
1865 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1866 wri_abspath, scratch_pool, scratch_pool));
1867 VERIFY_USABLE_WCROOT(wcroot);
1868 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1872 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1873 ibb.repos_root_url = repos_root_url;
1874 ibb.repos_uuid = repos_uuid;
1876 ibb.status = svn_wc__db_status_normal;
1877 ibb.kind = svn_node_file;
1878 ibb.repos_relpath = repos_relpath;
1879 ibb.revision = revision;
1882 ibb.changed_rev = changed_rev;
1883 ibb.changed_date = changed_date;
1884 ibb.changed_author = changed_author;
1886 ibb.checksum = checksum;
1888 ibb.dav_cache = dav_cache;
1889 ibb.iprops = new_iprops;
1891 if (update_actual_props)
1893 ibb.update_actual_props = TRUE;
1894 ibb.new_actual_props = new_actual_props;
1897 ibb.keep_recorded_info = keep_recorded_info;
1898 ibb.insert_base_deleted = insert_base_deleted;
1899 ibb.delete_working = delete_working;
1901 ibb.conflict = conflict;
1902 ibb.work_items = work_items;
1904 SVN_WC__DB_WITH_TXN(
1905 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1908 /* If this used to be a directory we should remove children so pass
1909 * depth infinity. */
1910 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1912 return SVN_NO_ERROR;
1917 svn_wc__db_base_add_symlink(svn_wc__db_t *db,
1918 const char *local_abspath,
1919 const char *wri_abspath,
1920 const char *repos_relpath,
1921 const char *repos_root_url,
1922 const char *repos_uuid,
1923 svn_revnum_t revision,
1924 const apr_hash_t *props,
1925 svn_revnum_t changed_rev,
1926 apr_time_t changed_date,
1927 const char *changed_author,
1929 apr_hash_t *dav_cache,
1930 svn_boolean_t delete_working,
1931 svn_boolean_t update_actual_props,
1932 apr_hash_t *new_actual_props,
1933 apr_array_header_t *new_iprops,
1934 svn_boolean_t keep_recorded_info,
1935 svn_boolean_t insert_base_deleted,
1936 const svn_skel_t *conflict,
1937 const svn_skel_t *work_items,
1938 apr_pool_t *scratch_pool)
1940 svn_wc__db_wcroot_t *wcroot;
1941 const char *local_relpath;
1942 insert_base_baton_t ibb;
1944 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1945 SVN_ERR_ASSERT(repos_relpath != NULL);
1946 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1947 SVN_ERR_ASSERT(repos_uuid != NULL);
1948 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1949 SVN_ERR_ASSERT(props != NULL);
1950 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1951 SVN_ERR_ASSERT(target != NULL);
1953 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1954 wri_abspath, scratch_pool, scratch_pool));
1955 VERIFY_USABLE_WCROOT(wcroot);
1956 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1959 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1960 ibb.repos_root_url = repos_root_url;
1961 ibb.repos_uuid = repos_uuid;
1963 ibb.status = svn_wc__db_status_normal;
1964 ibb.kind = svn_node_symlink;
1965 ibb.repos_relpath = repos_relpath;
1966 ibb.revision = revision;
1969 ibb.changed_rev = changed_rev;
1970 ibb.changed_date = changed_date;
1971 ibb.changed_author = changed_author;
1973 ibb.target = target;
1975 ibb.dav_cache = dav_cache;
1976 ibb.iprops = new_iprops;
1978 if (update_actual_props)
1980 ibb.update_actual_props = TRUE;
1981 ibb.new_actual_props = new_actual_props;
1984 ibb.keep_recorded_info = keep_recorded_info;
1985 ibb.insert_base_deleted = insert_base_deleted;
1986 ibb.delete_working = delete_working;
1988 ibb.conflict = conflict;
1989 ibb.work_items = work_items;
1991 SVN_WC__DB_WITH_TXN(
1992 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1995 /* If this used to be a directory we should remove children so pass
1996 * depth infinity. */
1997 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1999 return SVN_NO_ERROR;
2003 static svn_error_t *
2004 add_excluded_or_not_present_node(svn_wc__db_t *db,
2005 const char *local_abspath,
2006 const char *repos_relpath,
2007 const char *repos_root_url,
2008 const char *repos_uuid,
2009 svn_revnum_t revision,
2010 svn_node_kind_t kind,
2011 svn_wc__db_status_t status,
2012 const svn_skel_t *conflict,
2013 const svn_skel_t *work_items,
2014 apr_pool_t *scratch_pool)
2016 svn_wc__db_wcroot_t *wcroot;
2017 const char *local_relpath;
2018 insert_base_baton_t ibb;
2019 const char *dir_abspath, *name;
2021 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2022 SVN_ERR_ASSERT(repos_relpath != NULL);
2023 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
2024 SVN_ERR_ASSERT(repos_uuid != NULL);
2025 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
2026 SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
2027 || status == svn_wc__db_status_excluded
2028 || status == svn_wc__db_status_not_present);
2030 /* These absent presence nodes are only useful below a parent node that is
2031 present. To avoid problems with working copies obstructing the child
2032 we calculate the wcroot and local_relpath of the parent and then add
2035 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
2037 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2038 dir_abspath, scratch_pool, scratch_pool));
2039 VERIFY_USABLE_WCROOT(wcroot);
2041 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
2045 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
2046 ibb.repos_root_url = repos_root_url;
2047 ibb.repos_uuid = repos_uuid;
2049 ibb.status = status;
2051 ibb.repos_relpath = repos_relpath;
2052 ibb.revision = revision;
2054 /* Depending upon KIND, any of these might get used. */
2055 ibb.children = NULL;
2056 ibb.depth = svn_depth_unknown;
2057 ibb.checksum = NULL;
2060 ibb.conflict = conflict;
2061 ibb.work_items = work_items;
2063 SVN_WC__DB_WITH_TXN(
2064 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
2067 /* If this used to be a directory we should remove children so pass
2068 * depth infinity. */
2069 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2072 return SVN_NO_ERROR;
2077 svn_wc__db_base_add_excluded_node(svn_wc__db_t *db,
2078 const char *local_abspath,
2079 const char *repos_relpath,
2080 const char *repos_root_url,
2081 const char *repos_uuid,
2082 svn_revnum_t revision,
2083 svn_node_kind_t kind,
2084 svn_wc__db_status_t status,
2085 const svn_skel_t *conflict,
2086 const svn_skel_t *work_items,
2087 apr_pool_t *scratch_pool)
2089 SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
2090 || status == svn_wc__db_status_excluded);
2092 return add_excluded_or_not_present_node(
2093 db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2094 kind, status, conflict, work_items, scratch_pool);
2099 svn_wc__db_base_add_not_present_node(svn_wc__db_t *db,
2100 const char *local_abspath,
2101 const char *repos_relpath,
2102 const char *repos_root_url,
2103 const char *repos_uuid,
2104 svn_revnum_t revision,
2105 svn_node_kind_t kind,
2106 const svn_skel_t *conflict,
2107 const svn_skel_t *work_items,
2108 apr_pool_t *scratch_pool)
2110 return add_excluded_or_not_present_node(
2111 db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2112 kind, svn_wc__db_status_not_present, conflict, work_items, scratch_pool);
2115 /* Recursively clear moved-here information at the copy-half of the move
2116 * which moved the node at SRC_RELPATH away. This transforms the move into
2118 static svn_error_t *
2119 clear_moved_here(const char *src_relpath,
2120 svn_wc__db_wcroot_t *wcroot,
2121 apr_pool_t *scratch_pool)
2123 svn_sqlite__stmt_t *stmt;
2124 const char *dst_relpath;
2126 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
2127 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2128 src_relpath, relpath_depth(src_relpath)));
2129 SVN_ERR(svn_sqlite__step_row(stmt));
2130 dst_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
2131 SVN_ERR(svn_sqlite__reset(stmt));
2133 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2134 STMT_CLEAR_MOVED_HERE_RECURSIVE));
2135 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2136 dst_relpath, relpath_depth(dst_relpath)));
2137 SVN_ERR(svn_sqlite__step_done(stmt));
2139 return SVN_NO_ERROR;
2142 /* The body of svn_wc__db_base_remove().
2144 static svn_error_t *
2145 db_base_remove(svn_wc__db_wcroot_t *wcroot,
2146 const char *local_relpath,
2147 svn_wc__db_t *db, /* For checking conflicts */
2148 svn_boolean_t keep_as_working,
2149 svn_boolean_t queue_deletes,
2150 svn_boolean_t remove_locks,
2151 svn_revnum_t not_present_revision,
2152 svn_skel_t *conflict,
2153 svn_skel_t *work_items,
2154 apr_pool_t *scratch_pool)
2156 svn_sqlite__stmt_t *stmt;
2157 svn_boolean_t have_row;
2158 svn_wc__db_status_t status;
2159 apr_int64_t repos_id;
2160 const char *repos_relpath;
2161 svn_node_kind_t kind;
2162 svn_boolean_t keep_working;
2164 SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, NULL,
2165 &repos_relpath, &repos_id,
2166 NULL, NULL, NULL, NULL, NULL,
2167 NULL, NULL, NULL, NULL, NULL,
2168 wcroot, local_relpath,
2169 scratch_pool, scratch_pool));
2173 svn_sqlite__stmt_t *lock_stmt;
2175 SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
2176 STMT_DELETE_LOCK_RECURSIVELY));
2177 SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
2178 SVN_ERR(svn_sqlite__step_done(lock_stmt));
2181 if (status == svn_wc__db_status_normal
2184 SVN_ERR(svn_wc__db_op_make_copy(db,
2185 svn_dirent_join(wcroot->abspath,
2190 keep_working = TRUE;
2194 /* Check if there is already a working node */
2195 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2196 STMT_SELECT_WORKING_NODE));
2197 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2198 SVN_ERR(svn_sqlite__step(&keep_working, stmt));
2199 SVN_ERR(svn_sqlite__reset(stmt));
2202 /* Step 1: Create workqueue operations to remove files and dirs in the
2206 && (status == svn_wc__db_status_normal
2207 || status == svn_wc__db_status_incomplete))
2209 svn_skel_t *work_item;
2210 const char *local_abspath;
2212 local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
2214 if (kind == svn_node_dir)
2216 apr_pool_t *iterpool;
2217 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2218 STMT_SELECT_BASE_PRESENT));
2219 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2221 iterpool = svn_pool_create(scratch_pool);
2223 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2227 const char *node_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2228 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 1,
2230 const char *node_abspath;
2233 svn_pool_clear(iterpool);
2235 node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
2238 if (node_kind == svn_node_dir)
2239 err = svn_wc__wq_build_dir_remove(&work_item,
2240 db, wcroot->abspath,
2241 node_abspath, FALSE,
2242 iterpool, iterpool);
2244 err = svn_wc__wq_build_file_remove(&work_item,
2248 iterpool, iterpool);
2251 err = add_work_items(wcroot->sdb, work_item, iterpool);
2253 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2255 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2258 SVN_ERR(svn_sqlite__reset(stmt));
2260 SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
2261 db, wcroot->abspath,
2262 local_abspath, FALSE,
2263 scratch_pool, iterpool));
2264 svn_pool_destroy(iterpool);
2267 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
2268 db, wcroot->abspath,
2270 scratch_pool, scratch_pool));
2272 SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
2275 /* Step 2: Delete ACTUAL nodes */
2278 /* There won't be a record in NODE left for this node, so we want
2279 to remove *all* ACTUAL nodes, including ACTUAL ONLY. */
2280 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2281 STMT_DELETE_ACTUAL_NODE_RECURSIVE));
2282 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2283 SVN_ERR(svn_sqlite__step_done(stmt));
2285 else if (! keep_as_working)
2287 /* Delete only the ACTUAL nodes that apply to a delete of a BASE node */
2288 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2289 STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE));
2290 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2291 SVN_ERR(svn_sqlite__step_done(stmt));
2293 /* Else: Everything has been turned into a copy, so we want to keep all
2294 ACTUAL_NODE records */
2296 /* Step 3: Delete WORKING nodes */
2299 apr_pool_t *iterpool;
2302 * When deleting a conflicted node, moves of any moved-outside children
2303 * of the node must be broken. Else, the destination will still be marked
2304 * moved-here after the move source disappears from the working copy.
2306 * ### FIXME: It would be nicer to have the conflict resolver
2307 * break the move instead. It might also be a good idea to
2308 * flag a tree conflict on each moved-away child. But doing so
2309 * might introduce actual-only nodes without direct parents,
2310 * and we're not yet sure if other existing code is prepared
2311 * to handle such nodes. To be revisited post-1.8.
2313 * ### In case of a conflict we are most likely creating WORKING nodes
2314 * describing a copy of what was in BASE. The move information
2315 * should be updated to describe a move from the WORKING layer.
2316 * When stored that way the resolver of the tree conflict still has
2317 * the knowledge of what was moved.
2319 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2320 STMT_SELECT_MOVED_OUTSIDE));
2321 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2323 relpath_depth(local_relpath)));
2324 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2325 iterpool = svn_pool_create(scratch_pool);
2328 const char *child_relpath;
2331 svn_pool_clear(iterpool);
2332 child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
2333 err = clear_moved_here(child_relpath, wcroot, iterpool);
2335 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2336 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2338 svn_pool_destroy(iterpool);
2339 SVN_ERR(svn_sqlite__reset(stmt));
2343 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2344 STMT_DELETE_WORKING_BASE_DELETE));
2345 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2346 SVN_ERR(svn_sqlite__step_done(stmt));
2350 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2351 STMT_DELETE_WORKING_RECURSIVE));
2352 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2353 SVN_ERR(svn_sqlite__step_done(stmt));
2356 /* Step 4: Delete the BASE node descendants */
2357 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2358 STMT_DELETE_BASE_RECURSIVE));
2359 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2360 SVN_ERR(svn_sqlite__step_done(stmt));
2362 /* Step 5: handle the BASE node itself */
2363 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2364 STMT_DELETE_BASE_NODE));
2365 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2366 SVN_ERR(svn_sqlite__step_done(stmt));
2368 SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
2371 /* Step 6: Delete actual node if we don't keep working */
2374 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2375 STMT_DELETE_ACTUAL_NODE));
2376 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2377 SVN_ERR(svn_sqlite__step_done(stmt));
2380 if (SVN_IS_VALID_REVNUM(not_present_revision))
2382 struct insert_base_baton_t ibb;
2385 ibb.repos_id = repos_id;
2386 ibb.status = svn_wc__db_status_not_present;
2388 ibb.repos_relpath = repos_relpath;
2389 ibb.revision = not_present_revision;
2391 /* Depending upon KIND, any of these might get used. */
2392 ibb.children = NULL;
2393 ibb.depth = svn_depth_unknown;
2394 ibb.checksum = NULL;
2397 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
2400 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2402 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
2403 conflict, scratch_pool));
2405 return SVN_NO_ERROR;
2410 svn_wc__db_base_remove(svn_wc__db_t *db,
2411 const char *local_abspath,
2412 svn_boolean_t keep_as_working,
2413 svn_boolean_t queue_deletes,
2414 svn_boolean_t remove_locks,
2415 svn_revnum_t not_present_revision,
2416 svn_skel_t *conflict,
2417 svn_skel_t *work_items,
2418 apr_pool_t *scratch_pool)
2420 svn_wc__db_wcroot_t *wcroot;
2421 const char *local_relpath;
2423 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2425 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2426 local_abspath, scratch_pool, scratch_pool));
2427 VERIFY_USABLE_WCROOT(wcroot);
2429 SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath,
2430 db, keep_as_working, queue_deletes,
2431 remove_locks, not_present_revision,
2432 conflict, work_items, scratch_pool),
2435 /* If this used to be a directory we should remove children so pass
2436 * depth infinity. */
2437 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2440 return SVN_NO_ERROR;
2445 svn_wc__db_base_get_info_internal(svn_wc__db_status_t *status,
2446 svn_node_kind_t *kind,
2447 svn_revnum_t *revision,
2448 const char **repos_relpath,
2449 apr_int64_t *repos_id,
2450 svn_revnum_t *changed_rev,
2451 apr_time_t *changed_date,
2452 const char **changed_author,
2454 const svn_checksum_t **checksum,
2455 const char **target,
2456 svn_wc__db_lock_t **lock,
2457 svn_boolean_t *had_props,
2459 svn_boolean_t *update_root,
2460 svn_wc__db_wcroot_t *wcroot,
2461 const char *local_relpath,
2462 apr_pool_t *result_pool,
2463 apr_pool_t *scratch_pool)
2465 svn_sqlite__stmt_t *stmt;
2466 svn_boolean_t have_row;
2467 svn_error_t *err = SVN_NO_ERROR;
2469 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2470 lock ? STMT_SELECT_BASE_NODE_WITH_LOCK
2471 : STMT_SELECT_BASE_NODE));
2472 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2473 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2477 svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2479 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2487 *status = node_status;
2489 repos_location_from_columns(repos_id, revision, repos_relpath,
2490 stmt, 0, 4, 1, result_pool);
2491 SVN_ERR_ASSERT(!repos_id || *repos_id != INVALID_REPOS_ID);
2492 SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
2495 *lock = lock_from_columns(stmt, 15, 16, 17, 18, result_pool);
2499 *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2503 *changed_date = svn_sqlite__column_int64(stmt, 8);
2507 /* Result may be NULL. */
2508 *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2512 if (node_kind != svn_node_dir)
2514 *depth = svn_depth_unknown;
2518 *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2524 if (node_kind != svn_node_file)
2530 err = svn_sqlite__column_checksum(checksum, stmt, 5,
2533 err = svn_error_createf(
2535 _("The node '%s' has a corrupt checksum value."),
2536 path_for_error_message(wcroot, local_relpath,
2542 if (node_kind != svn_node_symlink)
2545 *target = svn_sqlite__column_text(stmt, 11, result_pool);
2549 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2553 if (node_status == svn_wc__db_status_normal
2554 || node_status == svn_wc__db_status_incomplete)
2556 SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2557 result_pool, scratch_pool));
2559 *props = apr_hash_make(result_pool);
2563 assert(svn_sqlite__column_is_null(stmt, 13));
2569 /* It's an update root iff it's a file external. */
2570 *update_root = svn_sqlite__column_boolean(stmt, 14);
2575 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2576 _("The node '%s' was not found."),
2577 path_for_error_message(wcroot, local_relpath,
2581 /* Note: given the composition, no need to wrap for tracing. */
2582 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2587 svn_wc__db_base_get_info(svn_wc__db_status_t *status,
2588 svn_node_kind_t *kind,
2589 svn_revnum_t *revision,
2590 const char **repos_relpath,
2591 const char **repos_root_url,
2592 const char **repos_uuid,
2593 svn_revnum_t *changed_rev,
2594 apr_time_t *changed_date,
2595 const char **changed_author,
2597 const svn_checksum_t **checksum,
2598 const char **target,
2599 svn_wc__db_lock_t **lock,
2600 svn_boolean_t *had_props,
2602 svn_boolean_t *update_root,
2604 const char *local_abspath,
2605 apr_pool_t *result_pool,
2606 apr_pool_t *scratch_pool)
2608 svn_wc__db_wcroot_t *wcroot;
2609 const char *local_relpath;
2610 apr_int64_t repos_id;
2612 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2614 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2615 local_abspath, scratch_pool, scratch_pool));
2616 VERIFY_USABLE_WCROOT(wcroot);
2618 SVN_ERR(svn_wc__db_base_get_info_internal(status, kind, revision,
2619 repos_relpath, &repos_id,
2620 changed_rev, changed_date,
2621 changed_author, depth,
2622 checksum, target, lock,
2623 had_props, props, update_root,
2624 wcroot, local_relpath,
2625 result_pool, scratch_pool));
2626 SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
2627 SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
2628 wcroot->sdb, repos_id, result_pool));
2630 return SVN_NO_ERROR;
2634 svn_wc__db_base_get_children_info(apr_hash_t **nodes,
2636 const char *dir_abspath,
2637 apr_pool_t *result_pool,
2638 apr_pool_t *scratch_pool)
2640 svn_wc__db_wcroot_t *wcroot;
2641 const char *local_relpath;
2642 svn_sqlite__stmt_t *stmt;
2643 svn_boolean_t have_row;
2645 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
2647 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2648 dir_abspath, scratch_pool, scratch_pool));
2649 VERIFY_USABLE_WCROOT(wcroot);
2651 *nodes = apr_hash_make(result_pool);
2653 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2654 STMT_SELECT_BASE_CHILDREN_INFO));
2655 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2657 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2661 struct svn_wc__db_base_info_t *info;
2663 apr_int64_t repos_id;
2664 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2665 const char *name = svn_relpath_basename(child_relpath, result_pool);
2667 info = apr_pcalloc(result_pool, sizeof(*info));
2669 repos_id = svn_sqlite__column_int64(stmt, 1);
2670 info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
2671 info->status = svn_sqlite__column_token(stmt, 3, presence_map);
2672 info->kind = svn_sqlite__column_token(stmt, 4, kind_map);
2673 info->revnum = svn_sqlite__column_revnum(stmt, 5);
2675 info->depth = svn_sqlite__column_token_null(stmt, 6, depth_map,
2678 info->update_root = svn_sqlite__column_boolean(stmt, 7);
2680 info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool);
2682 err = svn_wc__db_fetch_repos_info(&info->repos_root_url, NULL,
2683 wcroot->sdb, repos_id, result_pool);
2686 return svn_error_trace(
2687 svn_error_compose_create(err,
2688 svn_sqlite__reset(stmt)));
2691 svn_hash_sets(*nodes, name, info);
2693 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2696 SVN_ERR(svn_sqlite__reset(stmt));
2698 return SVN_NO_ERROR;
2703 svn_wc__db_base_get_props(apr_hash_t **props,
2705 const char *local_abspath,
2706 apr_pool_t *result_pool,
2707 apr_pool_t *scratch_pool)
2709 svn_wc__db_status_t presence;
2711 SVN_ERR(svn_wc__db_base_get_info(&presence, NULL, NULL, NULL, NULL,
2712 NULL, NULL, NULL, NULL, NULL,
2713 NULL, NULL, NULL, NULL, props, NULL,
2715 result_pool, scratch_pool));
2716 if (presence != svn_wc__db_status_normal
2717 && presence != svn_wc__db_status_incomplete)
2719 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2720 _("The node '%s' has a BASE status that"
2721 " has no properties."),
2722 svn_dirent_local_style(local_abspath,
2726 return SVN_NO_ERROR;
2731 svn_wc__db_base_get_children(const apr_array_header_t **children,
2733 const char *local_abspath,
2734 apr_pool_t *result_pool,
2735 apr_pool_t *scratch_pool)
2737 svn_wc__db_wcroot_t *wcroot;
2738 const char *local_relpath;
2740 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2742 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2744 scratch_pool, scratch_pool));
2745 VERIFY_USABLE_WCROOT(wcroot);
2747 return gather_repo_children(children, wcroot, local_relpath, 0,
2748 result_pool, scratch_pool);
2753 svn_wc__db_base_set_dav_cache(svn_wc__db_t *db,
2754 const char *local_abspath,
2755 const apr_hash_t *props,
2756 apr_pool_t *scratch_pool)
2758 svn_sqlite__stmt_t *stmt;
2761 SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
2762 STMT_UPDATE_BASE_NODE_DAV_CACHE,
2764 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
2766 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2768 if (affected_rows != 1)
2769 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2770 _("The node '%s' was not found."),
2771 svn_dirent_local_style(local_abspath,
2774 return SVN_NO_ERROR;
2779 svn_wc__db_base_get_dav_cache(apr_hash_t **props,
2781 const char *local_abspath,
2782 apr_pool_t *result_pool,
2783 apr_pool_t *scratch_pool)
2785 svn_sqlite__stmt_t *stmt;
2786 svn_boolean_t have_row;
2788 SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
2789 STMT_SELECT_BASE_DAV_CACHE, scratch_pool));
2790 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2792 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
2793 svn_sqlite__reset(stmt),
2794 _("The node '%s' was not found."),
2795 svn_dirent_local_style(local_abspath,
2798 SVN_ERR(svn_sqlite__column_properties(props, stmt, 0, result_pool,
2800 return svn_error_trace(svn_sqlite__reset(stmt));
2805 svn_wc__db_base_clear_dav_cache_recursive(svn_wc__db_t *db,
2806 const char *local_abspath,
2807 apr_pool_t *scratch_pool)
2809 svn_wc__db_wcroot_t *wcroot;
2810 const char *local_relpath;
2811 svn_sqlite__stmt_t *stmt;
2813 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2815 scratch_pool, scratch_pool));
2816 VERIFY_USABLE_WCROOT(wcroot);
2818 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2819 STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE));
2820 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2822 SVN_ERR(svn_sqlite__step_done(stmt));
2824 return SVN_NO_ERROR;
2829 svn_wc__db_depth_get_info(svn_wc__db_status_t *status,
2830 svn_node_kind_t *kind,
2831 svn_revnum_t *revision,
2832 const char **repos_relpath,
2833 apr_int64_t *repos_id,
2834 svn_revnum_t *changed_rev,
2835 apr_time_t *changed_date,
2836 const char **changed_author,
2838 const svn_checksum_t **checksum,
2839 const char **target,
2840 svn_boolean_t *had_props,
2842 svn_wc__db_wcroot_t *wcroot,
2843 const char *local_relpath,
2845 apr_pool_t *result_pool,
2846 apr_pool_t *scratch_pool)
2848 svn_sqlite__stmt_t *stmt;
2849 svn_boolean_t have_row;
2850 svn_error_t *err = SVN_NO_ERROR;
2852 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2853 STMT_SELECT_DEPTH_NODE));
2854 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
2855 wcroot->wc_id, local_relpath, op_depth));
2856 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2860 svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2862 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2870 *status = node_status;
2873 SVN_ERR(convert_to_working_status(status, *status));
2875 repos_location_from_columns(repos_id, revision, repos_relpath,
2876 stmt, 0, 4, 1, result_pool);
2880 *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2884 *changed_date = svn_sqlite__column_int64(stmt, 8);
2888 /* Result may be NULL. */
2889 *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2893 if (node_kind != svn_node_dir)
2895 *depth = svn_depth_unknown;
2899 *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2905 if (node_kind != svn_node_file)
2911 err = svn_sqlite__column_checksum(checksum, stmt, 5,
2914 err = svn_error_createf(
2916 _("The node '%s' has a corrupt checksum value."),
2917 path_for_error_message(wcroot, local_relpath,
2923 if (node_kind != svn_node_symlink)
2926 *target = svn_sqlite__column_text(stmt, 11, result_pool);
2930 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2934 if (node_status == svn_wc__db_status_normal
2935 || node_status == svn_wc__db_status_incomplete)
2937 SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2938 result_pool, scratch_pool));
2940 *props = apr_hash_make(result_pool);
2944 assert(svn_sqlite__column_is_null(stmt, 13));
2951 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2952 _("The node '%s' was not found."),
2953 path_for_error_message(wcroot, local_relpath,
2957 /* Note: given the composition, no need to wrap for tracing. */
2958 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2962 /* Baton for passing args to with_triggers(). */
2963 struct with_triggers_baton_t {
2966 svn_wc__db_txn_callback_t cb_func;
2970 /* Helper for creating SQLite triggers, running the main transaction
2971 callback, and then dropping the triggers. It guarantees that the
2972 triggers will not survive the transaction. This could be used for
2973 any general prefix/postscript statements where the postscript
2974 *must* be executed if the transaction completes.
2976 Implements svn_wc__db_txn_callback_t. */
2977 static svn_error_t *
2978 with_triggers(void *baton,
2979 svn_wc__db_wcroot_t *wcroot,
2980 const char *local_relpath,
2981 apr_pool_t *scratch_pool)
2983 struct with_triggers_baton_t *b = baton;
2987 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, b->create_trigger));
2989 err1 = b->cb_func(b->cb_baton, wcroot, local_relpath, scratch_pool);
2991 err2 = svn_sqlite__exec_statements(wcroot->sdb, b->drop_trigger);
2993 return svn_error_trace(svn_error_compose_create(err1, err2));
2997 /* Prototype for the "work callback" used by with_finalization(). */
2998 typedef svn_error_t * (*work_callback_t)(
3000 svn_wc__db_wcroot_t *wcroot,
3001 svn_cancel_func_t cancel_func,
3003 svn_wc_notify_func2_t notify_func,
3005 apr_pool_t *scratch_pool);
3007 /* Utility function to provide several features, with a guaranteed
3008 finalization (ie. to drop temporary tables).
3010 1) for WCROOT and LOCAL_RELPATH, run TXN_CB(TXN_BATON) within a
3012 2) if (1) is successful and a NOTIFY_FUNC is provided, then run
3013 the "work" step: WORK_CB(WORK_BATON).
3014 3) execute FINALIZE_STMT_IDX no matter what errors may be thrown
3015 from the above two steps.
3017 CANCEL_FUNC, CANCEL_BATON, NOTIFY_FUNC and NOTIFY_BATON are their
3018 typical values. These are passed to the work callback, which typically
3019 provides notification about the work done by TXN_CB. */
3020 static svn_error_t *
3021 with_finalization(svn_wc__db_wcroot_t *wcroot,
3022 const char *local_relpath,
3023 svn_wc__db_txn_callback_t txn_cb,
3025 work_callback_t work_cb,
3027 svn_cancel_func_t cancel_func,
3029 svn_wc_notify_func2_t notify_func,
3031 int finalize_stmt_idx,
3032 apr_pool_t *scratch_pool)
3037 err1 = svn_wc__db_with_txn(wcroot, local_relpath, txn_cb, txn_baton,
3040 if (err1 == NULL && notify_func != NULL)
3042 err2 = work_cb(work_baton, wcroot,
3043 cancel_func, cancel_baton,
3044 notify_func, notify_baton,
3046 err1 = svn_error_compose_create(err1, err2);
3049 err2 = svn_sqlite__exec_statements(wcroot->sdb, finalize_stmt_idx);
3051 return svn_error_trace(svn_error_compose_create(err1, err2));
3055 /* Initialize the baton with appropriate "blank" values. This allows the
3056 insertion function to leave certain columns null. */
3058 blank_ieb(insert_external_baton_t *ieb)
3060 memset(ieb, 0, sizeof(*ieb));
3061 ieb->revision = SVN_INVALID_REVNUM;
3062 ieb->changed_rev = SVN_INVALID_REVNUM;
3063 ieb->repos_id = INVALID_REPOS_ID;
3065 ieb->recorded_peg_revision = SVN_INVALID_REVNUM;
3066 ieb->recorded_revision = SVN_INVALID_REVNUM;
3069 /* Insert the externals row represented by (insert_external_baton_t *) BATON.
3071 * Implements svn_wc__db_txn_callback_t. */
3072 static svn_error_t *
3073 insert_external_node(const insert_external_baton_t *ieb,
3074 svn_wc__db_wcroot_t *wcroot,
3075 const char *local_relpath,
3076 apr_pool_t *scratch_pool)
3078 svn_wc__db_status_t status;
3080 svn_boolean_t update_root;
3081 apr_int64_t repos_id;
3082 svn_sqlite__stmt_t *stmt;
3084 if (ieb->repos_id != INVALID_REPOS_ID)
3085 repos_id = ieb->repos_id;
3087 SVN_ERR(create_repos_id(&repos_id, ieb->repos_root_url, ieb->repos_uuid,
3088 wcroot->sdb, scratch_pool));
3090 /* And there must be no existing BASE node or it must be a file external */
3091 err = svn_wc__db_base_get_info_internal(&status, NULL, NULL, NULL, NULL,
3092 NULL, NULL, NULL, NULL, NULL,
3093 NULL, NULL, NULL, NULL, &update_root,
3094 wcroot, local_relpath,
3095 scratch_pool, scratch_pool);
3098 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3099 return svn_error_trace(err);
3101 svn_error_clear(err);
3103 else if (status == svn_wc__db_status_normal && !update_root)
3104 return svn_error_create(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, NULL);
3106 if (ieb->kind == svn_node_file
3107 || ieb->kind == svn_node_symlink)
3109 struct insert_base_baton_t ibb;
3113 ibb.status = svn_wc__db_status_normal;
3114 ibb.kind = ieb->kind;
3116 ibb.repos_id = repos_id;
3117 ibb.repos_relpath = ieb->repos_relpath;
3118 ibb.revision = ieb->revision;
3120 ibb.props = ieb->props;
3121 ibb.iprops = ieb->iprops;
3122 ibb.changed_rev = ieb->changed_rev;
3123 ibb.changed_date = ieb->changed_date;
3124 ibb.changed_author = ieb->changed_author;
3126 ibb.dav_cache = ieb->dav_cache;
3128 ibb.checksum = ieb->checksum;
3129 ibb.target = ieb->target;
3131 ibb.conflict = ieb->conflict;
3133 ibb.update_actual_props = ieb->update_actual_props;
3134 ibb.new_actual_props = ieb->new_actual_props;
3136 ibb.keep_recorded_info = ieb->keep_recorded_info;
3138 ibb.work_items = ieb->work_items;
3140 ibb.file_external = TRUE;
3142 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
3145 SVN_ERR(add_work_items(wcroot->sdb, ieb->work_items, scratch_pool));
3147 /* The externals table only support presence normal and excluded */
3148 SVN_ERR_ASSERT(ieb->presence == svn_wc__db_status_normal
3149 || ieb->presence == svn_wc__db_status_excluded);
3151 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_EXTERNAL));
3153 SVN_ERR(svn_sqlite__bindf(stmt, "issttsis",
3156 svn_relpath_dirname(local_relpath,
3158 presence_map, ieb->presence,
3159 kind_map, ieb->kind,
3160 ieb->record_ancestor_relpath,
3162 ieb->recorded_repos_relpath));
3164 if (SVN_IS_VALID_REVNUM(ieb->recorded_peg_revision))
3165 SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, ieb->recorded_peg_revision));
3167 if (SVN_IS_VALID_REVNUM(ieb->recorded_revision))
3168 SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, ieb->recorded_revision));
3170 SVN_ERR(svn_sqlite__insert(NULL, stmt));
3172 return SVN_NO_ERROR;
3176 svn_wc__db_external_add_file(svn_wc__db_t *db,
3177 const char *local_abspath,
3178 const char *wri_abspath,
3180 const char *repos_relpath,
3181 const char *repos_root_url,
3182 const char *repos_uuid,
3183 svn_revnum_t revision,
3185 const apr_hash_t *props,
3186 apr_array_header_t *iprops,
3188 svn_revnum_t changed_rev,
3189 apr_time_t changed_date,
3190 const char *changed_author,
3192 const svn_checksum_t *checksum,
3194 const apr_hash_t *dav_cache,
3196 const char *record_ancestor_abspath,
3197 const char *recorded_repos_relpath,
3198 svn_revnum_t recorded_peg_revision,
3199 svn_revnum_t recorded_revision,
3201 svn_boolean_t update_actual_props,
3202 apr_hash_t *new_actual_props,
3204 svn_boolean_t keep_recorded_info,
3205 const svn_skel_t *conflict,
3206 const svn_skel_t *work_items,
3207 apr_pool_t *scratch_pool)
3209 svn_wc__db_wcroot_t *wcroot;
3210 const char *local_relpath;
3211 insert_external_baton_t ieb;
3213 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3216 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3218 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3219 wri_abspath, scratch_pool, scratch_pool));
3220 VERIFY_USABLE_WCROOT(wcroot);
3222 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3223 record_ancestor_abspath));
3225 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3227 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3231 ieb.kind = svn_node_file;
3232 ieb.presence = svn_wc__db_status_normal;
3234 ieb.repos_root_url = repos_root_url;
3235 ieb.repos_uuid = repos_uuid;
3237 ieb.repos_relpath = repos_relpath;
3238 ieb.revision = revision;
3241 ieb.iprops = iprops;
3243 ieb.changed_rev = changed_rev;
3244 ieb.changed_date = changed_date;
3245 ieb.changed_author = changed_author;
3247 ieb.checksum = checksum;
3249 ieb.dav_cache = dav_cache;
3251 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3253 record_ancestor_abspath);
3254 ieb.recorded_repos_relpath = recorded_repos_relpath;
3255 ieb.recorded_peg_revision = recorded_peg_revision;
3256 ieb.recorded_revision = recorded_revision;
3258 ieb.update_actual_props = update_actual_props;
3259 ieb.new_actual_props = new_actual_props;
3261 ieb.keep_recorded_info = keep_recorded_info;
3263 ieb.conflict = conflict;
3264 ieb.work_items = work_items;
3266 SVN_WC__DB_WITH_TXN(
3267 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3270 return SVN_NO_ERROR;
3274 svn_wc__db_external_add_symlink(svn_wc__db_t *db,
3275 const char *local_abspath,
3276 const char *wri_abspath,
3277 const char *repos_relpath,
3278 const char *repos_root_url,
3279 const char *repos_uuid,
3280 svn_revnum_t revision,
3281 const apr_hash_t *props,
3282 svn_revnum_t changed_rev,
3283 apr_time_t changed_date,
3284 const char *changed_author,
3286 const apr_hash_t *dav_cache,
3287 const char *record_ancestor_abspath,
3288 const char *recorded_repos_relpath,
3289 svn_revnum_t recorded_peg_revision,
3290 svn_revnum_t recorded_revision,
3291 svn_boolean_t update_actual_props,
3292 apr_hash_t *new_actual_props,
3293 svn_boolean_t keep_recorded_info,
3294 const svn_skel_t *work_items,
3295 apr_pool_t *scratch_pool)
3297 svn_wc__db_wcroot_t *wcroot;
3298 const char *local_relpath;
3299 insert_external_baton_t ieb;
3301 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3304 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3306 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3307 wri_abspath, scratch_pool, scratch_pool));
3308 VERIFY_USABLE_WCROOT(wcroot);
3310 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3311 record_ancestor_abspath));
3313 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3315 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3319 ieb.kind = svn_node_symlink;
3320 ieb.presence = svn_wc__db_status_normal;
3322 ieb.repos_root_url = repos_root_url;
3323 ieb.repos_uuid = repos_uuid;
3325 ieb.repos_relpath = repos_relpath;
3326 ieb.revision = revision;
3330 ieb.changed_rev = changed_rev;
3331 ieb.changed_date = changed_date;
3332 ieb.changed_author = changed_author;
3334 ieb.target = target;
3336 ieb.dav_cache = dav_cache;
3338 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3340 record_ancestor_abspath);
3341 ieb.recorded_repos_relpath = recorded_repos_relpath;
3342 ieb.recorded_peg_revision = recorded_peg_revision;
3343 ieb.recorded_revision = recorded_revision;
3345 ieb.update_actual_props = update_actual_props;
3346 ieb.new_actual_props = new_actual_props;
3348 ieb.keep_recorded_info = keep_recorded_info;
3350 ieb.work_items = work_items;
3352 SVN_WC__DB_WITH_TXN(
3353 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3356 return SVN_NO_ERROR;
3360 svn_wc__db_external_add_dir(svn_wc__db_t *db,
3361 const char *local_abspath,
3362 const char *wri_abspath,
3363 const char *repos_root_url,
3364 const char *repos_uuid,
3365 const char *record_ancestor_abspath,
3366 const char *recorded_repos_relpath,
3367 svn_revnum_t recorded_peg_revision,
3368 svn_revnum_t recorded_revision,
3369 const svn_skel_t *work_items,
3370 apr_pool_t *scratch_pool)
3372 svn_wc__db_wcroot_t *wcroot;
3373 const char *local_relpath;
3374 insert_external_baton_t ieb;
3376 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3379 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3381 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3382 wri_abspath, scratch_pool, scratch_pool));
3383 VERIFY_USABLE_WCROOT(wcroot);
3385 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3386 record_ancestor_abspath));
3388 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3390 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3394 ieb.kind = svn_node_dir;
3395 ieb.presence = svn_wc__db_status_normal;
3397 ieb.repos_root_url = repos_root_url;
3398 ieb.repos_uuid = repos_uuid;
3400 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3402 record_ancestor_abspath);
3403 ieb.recorded_repos_relpath = recorded_repos_relpath;
3404 ieb.recorded_peg_revision = recorded_peg_revision;
3405 ieb.recorded_revision = recorded_revision;
3407 ieb.work_items = work_items;
3409 SVN_WC__DB_WITH_TXN(
3410 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3413 return SVN_NO_ERROR;
3416 /* The body of svn_wc__db_external_remove(). */
3417 static svn_error_t *
3418 db_external_remove(const svn_skel_t *work_items,
3419 svn_wc__db_wcroot_t *wcroot,
3420 const char *local_relpath,
3421 apr_pool_t *scratch_pool)
3423 svn_sqlite__stmt_t *stmt;
3425 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3426 STMT_DELETE_EXTERNAL));
3427 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3428 SVN_ERR(svn_sqlite__step_done(stmt));
3430 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
3432 /* ### What about actual? */
3433 return SVN_NO_ERROR;
3437 svn_wc__db_external_remove(svn_wc__db_t *db,
3438 const char *local_abspath,
3439 const char *wri_abspath,
3440 const svn_skel_t *work_items,
3441 apr_pool_t *scratch_pool)
3443 svn_wc__db_wcroot_t *wcroot;
3444 const char *local_relpath;
3446 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3449 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3451 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3452 wri_abspath, scratch_pool, scratch_pool));
3453 VERIFY_USABLE_WCROOT(wcroot);
3455 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3457 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3459 SVN_WC__DB_WITH_TXN(db_external_remove(work_items, wcroot, local_relpath,
3463 return SVN_NO_ERROR;
3467 svn_wc__db_external_read(svn_wc__db_status_t *status,
3468 svn_node_kind_t *kind,
3469 const char **definining_abspath,
3470 const char **repos_root_url,
3471 const char **repos_uuid,
3472 const char **recorded_repos_relpath,
3473 svn_revnum_t *recorded_peg_revision,
3474 svn_revnum_t *recorded_revision,
3476 const char *local_abspath,
3477 const char *wri_abspath,
3478 apr_pool_t *result_pool,
3479 apr_pool_t *scratch_pool)
3481 svn_wc__db_wcroot_t *wcroot;
3482 const char *local_relpath;
3483 svn_sqlite__stmt_t *stmt;
3484 svn_boolean_t have_info;
3485 svn_error_t *err = NULL;
3486 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3489 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3491 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3492 wri_abspath, scratch_pool, scratch_pool));
3493 VERIFY_USABLE_WCROOT(wcroot);
3495 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3497 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3499 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3500 STMT_SELECT_EXTERNAL_INFO));
3501 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3502 SVN_ERR(svn_sqlite__step(&have_info, stmt));
3507 *status = svn_sqlite__column_token(stmt, 0, presence_map);
3510 *kind = svn_sqlite__column_token(stmt, 1, kind_map);
3512 if (definining_abspath)
3514 const char *record_relpath = svn_sqlite__column_text(stmt, 2, NULL);
3516 *definining_abspath = svn_dirent_join(wcroot->abspath,
3517 record_relpath, result_pool);
3520 if (repos_root_url || repos_uuid)
3522 apr_int64_t repos_id;
3524 repos_id = svn_sqlite__column_int64(stmt, 3);
3526 err = svn_error_compose_create(
3528 svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
3529 wcroot->sdb, repos_id,
3533 if (recorded_repos_relpath)
3534 *recorded_repos_relpath = svn_sqlite__column_text(stmt, 4,
3537 if (recorded_peg_revision)
3538 *recorded_peg_revision = svn_sqlite__column_revnum(stmt, 5);
3540 if (recorded_revision)
3541 *recorded_revision = svn_sqlite__column_revnum(stmt, 6);
3545 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3546 _("The node '%s' is not an external."),
3547 svn_dirent_local_style(local_abspath,
3551 return svn_error_trace(
3552 svn_error_compose_create(err, svn_sqlite__reset(stmt)));
3556 svn_wc__db_committable_externals_below(apr_array_header_t **externals,
3558 const char *local_abspath,
3559 svn_boolean_t immediates_only,
3560 apr_pool_t *result_pool,
3561 apr_pool_t *scratch_pool)
3563 svn_wc__db_wcroot_t *wcroot;
3564 svn_sqlite__stmt_t *stmt;
3565 const char *local_relpath;
3566 svn_boolean_t have_row;
3567 svn_wc__committable_external_info_t *info;
3568 svn_node_kind_t db_kind;
3569 apr_array_header_t *result = NULL;
3571 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3573 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3574 local_abspath, scratch_pool, scratch_pool));
3575 VERIFY_USABLE_WCROOT(wcroot);
3577 SVN_ERR(svn_sqlite__get_statement(
3580 ? STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW
3581 : STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW));
3583 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3585 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3588 result = apr_array_make(result_pool, 0,
3589 sizeof(svn_wc__committable_external_info_t *));
3593 info = apr_palloc(result_pool, sizeof(*info));
3595 local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3596 info->local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
3599 db_kind = svn_sqlite__column_token(stmt, 1, kind_map);
3600 SVN_ERR_ASSERT(db_kind == svn_node_file || db_kind == svn_node_dir);
3601 info->kind = db_kind;
3603 info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
3604 info->repos_root_url = svn_sqlite__column_text(stmt, 3, result_pool);
3606 APR_ARRAY_PUSH(result, svn_wc__committable_external_info_t *) = info;
3608 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3611 *externals = result;
3612 return svn_error_trace(svn_sqlite__reset(stmt));
3616 svn_wc__db_externals_defined_below(apr_hash_t **externals,
3618 const char *local_abspath,
3619 apr_pool_t *result_pool,
3620 apr_pool_t *scratch_pool)
3622 svn_wc__db_wcroot_t *wcroot;
3623 svn_sqlite__stmt_t *stmt;
3624 const char *local_relpath;
3625 svn_boolean_t have_row;
3627 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3629 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3630 local_abspath, scratch_pool, scratch_pool));
3631 VERIFY_USABLE_WCROOT(wcroot);
3633 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3634 STMT_SELECT_EXTERNALS_DEFINED));
3636 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3638 *externals = apr_hash_make(result_pool);
3639 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3643 const char *def_local_relpath;
3645 local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3646 def_local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3648 svn_hash_sets(*externals,
3649 svn_dirent_join(wcroot->abspath, local_relpath,
3651 svn_dirent_join(wcroot->abspath, def_local_relpath,
3654 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3657 return svn_error_trace(svn_sqlite__reset(stmt));
3661 svn_wc__db_externals_gather_definitions(apr_hash_t **externals,
3662 apr_hash_t **depths,
3664 const char *local_abspath,
3665 apr_pool_t *result_pool,
3666 apr_pool_t *scratch_pool)
3668 svn_wc__db_wcroot_t *wcroot;
3669 svn_sqlite__stmt_t *stmt;
3670 const char *local_relpath;
3671 svn_boolean_t have_row;
3672 svn_error_t *err = NULL;
3673 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3675 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3677 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3678 local_abspath, scratch_pool, iterpool));
3679 VERIFY_USABLE_WCROOT(wcroot);
3681 *externals = apr_hash_make(result_pool);
3683 *depths = apr_hash_make(result_pool);
3685 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3686 STMT_SELECT_EXTERNAL_PROPERTIES));
3688 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3690 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3694 apr_hash_t *node_props;
3695 const char *external_value;
3697 svn_pool_clear(iterpool);
3698 err = svn_sqlite__column_properties(&node_props, stmt, 0, iterpool,
3704 external_value = svn_prop_get_value(node_props, SVN_PROP_EXTERNALS);
3708 const char *node_abspath;
3709 const char *node_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3711 node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
3714 svn_hash_sets(*externals, node_abspath,
3715 apr_pstrdup(result_pool, external_value));
3720 = svn_sqlite__column_token_null(stmt, 2, depth_map,
3723 svn_hash_sets(*depths, node_abspath,
3724 /* Use static string */
3725 svn_token__to_word(depth_map, depth));
3729 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3732 svn_pool_destroy(iterpool);
3734 return svn_error_trace(svn_error_compose_create(err,
3735 svn_sqlite__reset(stmt)));
3738 /* Copy the ACTUAL data for SRC_RELPATH and tweak it to refer to DST_RELPATH.
3739 The new ACTUAL data won't have any conflicts. */
3740 static svn_error_t *
3741 copy_actual(svn_wc__db_wcroot_t *src_wcroot,
3742 const char *src_relpath,
3743 svn_wc__db_wcroot_t *dst_wcroot,
3744 const char *dst_relpath,
3745 apr_pool_t *scratch_pool)
3747 svn_sqlite__stmt_t *stmt;
3748 svn_boolean_t have_row;
3750 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
3751 STMT_SELECT_ACTUAL_NODE));
3752 SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
3753 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3756 apr_size_t props_size;
3757 const char *changelist;
3758 const char *properties;
3760 /* Skipping conflict data... */
3761 changelist = svn_sqlite__column_text(stmt, 0, scratch_pool);
3762 /* No need to parse the properties when simply copying. */
3763 properties = svn_sqlite__column_blob(stmt, 1, &props_size, scratch_pool);
3765 if (changelist || properties)
3767 SVN_ERR(svn_sqlite__reset(stmt));
3769 SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
3770 STMT_INSERT_ACTUAL_NODE));
3771 SVN_ERR(svn_sqlite__bindf(stmt, "issbs",
3772 dst_wcroot->wc_id, dst_relpath,
3773 svn_relpath_dirname(dst_relpath, scratch_pool),
3774 properties, props_size, changelist));
3775 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3778 SVN_ERR(svn_sqlite__reset(stmt));
3780 return SVN_NO_ERROR;
3783 /* Helper for svn_wc__db_op_copy to handle copying from one db to
3785 static svn_error_t *
3786 cross_db_copy(svn_wc__db_wcroot_t *src_wcroot,
3787 const char *src_relpath,
3788 svn_wc__db_wcroot_t *dst_wcroot,
3789 const char *dst_relpath,
3790 svn_wc__db_status_t dst_status,
3792 int dst_np_op_depth,
3793 svn_node_kind_t kind,
3794 const apr_array_header_t *children,
3795 apr_int64_t copyfrom_id,
3796 const char *copyfrom_relpath,
3797 svn_revnum_t copyfrom_rev,
3798 apr_pool_t *scratch_pool)
3800 insert_working_baton_t iwb;
3801 svn_revnum_t changed_rev;
3802 apr_time_t changed_date;
3803 const char *changed_author;
3804 const svn_checksum_t *checksum;
3808 SVN_ERR_ASSERT(kind == svn_node_file
3809 || kind == svn_node_dir
3812 SVN_ERR(read_info(NULL, NULL, NULL, NULL, NULL,
3813 &changed_rev, &changed_date, &changed_author, &depth,
3814 &checksum, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3815 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3816 src_wcroot, src_relpath, scratch_pool, scratch_pool));
3818 if (dst_status != svn_wc__db_status_not_present
3819 && dst_status != svn_wc__db_status_excluded
3820 && dst_status != svn_wc__db_status_server_excluded)
3822 SVN_ERR(db_read_pristine_props(&props, src_wcroot, src_relpath, FALSE,
3823 scratch_pool, scratch_pool));
3829 iwb.presence = dst_status;
3833 iwb.changed_rev = changed_rev;
3834 iwb.changed_date = changed_date;
3835 iwb.changed_author = changed_author;
3836 iwb.original_repos_id = copyfrom_id;
3837 iwb.original_repos_relpath = copyfrom_relpath;
3838 iwb.original_revnum = copyfrom_rev;
3839 iwb.moved_here = FALSE;
3841 iwb.op_depth = dst_op_depth;
3843 iwb.checksum = checksum;
3844 iwb.children = children;
3847 iwb.not_present_op_depth = dst_np_op_depth;
3849 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath, scratch_pool));
3851 SVN_ERR(copy_actual(src_wcroot, src_relpath,
3852 dst_wcroot, dst_relpath, scratch_pool));
3854 return SVN_NO_ERROR;
3857 /* Helper for scan_deletion_txn. Extracts the moved-to information, if
3858 any, from STMT. Sets *SCAN to FALSE if moved-to was available. */
3859 static svn_error_t *
3860 get_moved_to(const char **moved_to_relpath_p,
3861 const char **moved_to_op_root_relpath_p,
3862 svn_boolean_t *scan,
3863 svn_sqlite__stmt_t *stmt,
3864 const char *current_relpath,
3865 svn_wc__db_wcroot_t *wcroot,
3866 const char *local_relpath,
3867 apr_pool_t *result_pool,
3868 apr_pool_t *scratch_pool)
3870 const char *moved_to_relpath = svn_sqlite__column_text(stmt, 3, NULL);
3872 if (moved_to_relpath)
3874 const char *moved_to_op_root_relpath = moved_to_relpath;
3876 if (strcmp(current_relpath, local_relpath))
3878 /* LOCAL_RELPATH is a child inside the move op-root. */
3879 const char *moved_child_relpath;
3881 /* The CURRENT_RELPATH is the op_root of the delete-half of
3882 * the move. LOCAL_RELPATH is a child that was moved along.
3883 * Compute the child's new location within the move target. */
3884 moved_child_relpath = svn_relpath_skip_ancestor(current_relpath,
3886 SVN_ERR_ASSERT(moved_child_relpath &&
3887 strlen(moved_child_relpath) > 0);
3888 moved_to_relpath = svn_relpath_join(moved_to_op_root_relpath,
3889 moved_child_relpath,
3893 if (moved_to_op_root_relpath && moved_to_op_root_relpath_p)
3894 *moved_to_op_root_relpath_p
3895 = apr_pstrdup(result_pool, moved_to_op_root_relpath);
3897 if (moved_to_relpath && moved_to_relpath_p)
3899 = apr_pstrdup(result_pool, moved_to_relpath);
3904 return SVN_NO_ERROR;
3908 /* The body of svn_wc__db_scan_deletion().
3910 static svn_error_t *
3911 scan_deletion_txn(const char **base_del_relpath,
3912 const char **moved_to_relpath,
3913 const char **work_del_relpath,
3914 const char **moved_to_op_root_relpath,
3915 svn_wc__db_wcroot_t *wcroot,
3916 const char *local_relpath,
3917 apr_pool_t *result_pool,
3918 apr_pool_t *scratch_pool)
3920 const char *current_relpath = local_relpath;
3921 svn_sqlite__stmt_t *stmt;
3922 svn_wc__db_status_t work_presence;
3923 svn_boolean_t have_row, scan, have_base;
3926 /* Initialize all the OUT parameters. */
3927 if (base_del_relpath != NULL)
3928 *base_del_relpath = NULL;
3929 if (moved_to_relpath != NULL)
3930 *moved_to_relpath = NULL;
3931 if (work_del_relpath != NULL)
3932 *work_del_relpath = NULL;
3933 if (moved_to_op_root_relpath != NULL)
3934 *moved_to_op_root_relpath = NULL;
3936 /* If looking for moved-to info then we need to scan every path
3937 until we find it. If not looking for moved-to we only need to
3938 check op-roots and parents of op-roots. */
3939 scan = (moved_to_op_root_relpath || moved_to_relpath);
3941 SVN_ERR(svn_sqlite__get_statement(
3943 scan ? STMT_SELECT_DELETION_INFO_SCAN
3944 : STMT_SELECT_DELETION_INFO));
3946 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
3947 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3949 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
3950 _("The node '%s' was not found."),
3951 path_for_error_message(wcroot, local_relpath,
3954 work_presence = svn_sqlite__column_token(stmt, 1, presence_map);
3955 have_base = !svn_sqlite__column_is_null(stmt, 0);
3956 if (work_presence != svn_wc__db_status_not_present
3957 && work_presence != svn_wc__db_status_base_deleted)
3958 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
3959 svn_sqlite__reset(stmt),
3960 _("Expected node '%s' to be deleted."),
3961 path_for_error_message(wcroot, local_relpath,
3964 op_depth = svn_sqlite__column_int(stmt, 2);
3966 /* Special case: LOCAL_RELPATH not-present within a WORKING tree, we
3967 treat this as an op-root. At commit time we need to explicitly
3968 delete such nodes otherwise they will be present in the
3970 if (work_presence == svn_wc__db_status_not_present
3971 && work_del_relpath && !*work_del_relpath)
3973 *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
3975 if (!scan && !base_del_relpath)
3977 /* We have all we need, exit early */
3978 SVN_ERR(svn_sqlite__reset(stmt));
3979 return SVN_NO_ERROR;
3987 const char *parent_relpath;
3988 int current_depth = relpath_depth(current_relpath);
3990 /* Step CURRENT_RELPATH to op-root */
3996 err = get_moved_to(moved_to_relpath, moved_to_op_root_relpath,
3997 &scan, stmt, current_relpath,
3998 wcroot, local_relpath,
3999 result_pool, scratch_pool);
4001 && !base_del_relpath
4002 && !work_del_relpath))
4004 /* We have all we need (or an error occurred) */
4005 SVN_ERR(svn_sqlite__reset(stmt));
4006 return svn_error_trace(err);
4010 if (current_depth <= op_depth)
4013 current_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4016 if (scan || current_depth == op_depth)
4018 SVN_ERR(svn_sqlite__reset(stmt));
4019 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
4021 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4022 SVN_ERR_ASSERT(have_row);
4023 have_base = !svn_sqlite__column_is_null(stmt, 0);
4026 SVN_ERR(svn_sqlite__reset(stmt));
4028 /* Now CURRENT_RELPATH is an op-root, have a look at the parent. */
4030 SVN_ERR_ASSERT(current_relpath[0] != '\0'); /* Catch invalid data */
4031 parent_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4032 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4033 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4036 /* No row means no WORKING node which mean we just fell off
4037 the WORKING tree, so CURRENT_RELPATH is the op-root
4038 closest to the wc root. */
4039 if (have_base && base_del_relpath)
4040 *base_del_relpath = apr_pstrdup(result_pool, current_relpath);
4044 /* Still in the WORKING tree so the first time we get here
4045 CURRENT_RELPATH is a delete op-root in the WORKING tree. */
4046 if (work_del_relpath && !*work_del_relpath)
4048 *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
4050 if (!scan && !base_del_relpath)
4051 break; /* We have all we need */
4054 current_relpath = parent_relpath;
4055 op_depth = svn_sqlite__column_int(stmt, 2);
4056 have_base = !svn_sqlite__column_is_null(stmt, 0);
4059 SVN_ERR(svn_sqlite__reset(stmt));
4061 return SVN_NO_ERROR;
4065 svn_wc__db_scan_deletion(const char **base_del_abspath,
4066 const char **moved_to_abspath,
4067 const char **work_del_abspath,
4068 const char **moved_to_op_root_abspath,
4070 const char *local_abspath,
4071 apr_pool_t *result_pool,
4072 apr_pool_t *scratch_pool)
4074 svn_wc__db_wcroot_t *wcroot;
4075 const char *local_relpath;
4076 const char *base_del_relpath, *moved_to_relpath, *work_del_relpath;
4077 const char *moved_to_op_root_relpath;
4079 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4081 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4082 local_abspath, scratch_pool, scratch_pool));
4083 VERIFY_USABLE_WCROOT(wcroot);
4085 SVN_WC__DB_WITH_TXN(
4086 scan_deletion_txn(&base_del_relpath, &moved_to_relpath,
4087 &work_del_relpath, &moved_to_op_root_relpath,
4088 wcroot, local_relpath, result_pool, scratch_pool),
4091 if (base_del_abspath)
4093 *base_del_abspath = (base_del_relpath
4094 ? svn_dirent_join(wcroot->abspath,
4095 base_del_relpath, result_pool)
4098 if (moved_to_abspath)
4100 *moved_to_abspath = (moved_to_relpath
4101 ? svn_dirent_join(wcroot->abspath,
4102 moved_to_relpath, result_pool)
4105 if (work_del_abspath)
4107 *work_del_abspath = (work_del_relpath
4108 ? svn_dirent_join(wcroot->abspath,
4109 work_del_relpath, result_pool)
4112 if (moved_to_op_root_abspath)
4114 *moved_to_op_root_abspath = (moved_to_op_root_relpath
4115 ? svn_dirent_join(wcroot->abspath,
4116 moved_to_op_root_relpath,
4121 return SVN_NO_ERROR;
4125 /* Set *COPYFROM_ID, *COPYFROM_RELPATH, *COPYFROM_REV to the values
4126 appropriate for the copy. Also return *STATUS, *KIND and *HAVE_WORK, *OP_ROOT
4127 since they are available. This is a helper for
4128 svn_wc__db_op_copy. */
4129 static svn_error_t *
4130 get_info_for_copy(apr_int64_t *copyfrom_id,
4131 const char **copyfrom_relpath,
4132 svn_revnum_t *copyfrom_rev,
4133 svn_wc__db_status_t *status,
4134 svn_node_kind_t *kind,
4135 svn_boolean_t *op_root,
4136 svn_wc__db_wcroot_t *src_wcroot,
4137 const char *local_relpath,
4138 svn_wc__db_wcroot_t *dst_wcroot,
4139 apr_pool_t *result_pool,
4140 apr_pool_t *scratch_pool)
4142 const char *repos_relpath;
4143 svn_revnum_t revision;
4144 svn_wc__db_status_t node_status;
4145 apr_int64_t repos_id;
4146 svn_boolean_t is_op_root;
4148 SVN_ERR(read_info(&node_status, kind, &revision, &repos_relpath, &repos_id,
4149 NULL, NULL, NULL, NULL, NULL, NULL, copyfrom_relpath,
4150 copyfrom_id, copyfrom_rev, NULL, NULL, NULL, NULL,
4151 NULL, &is_op_root, NULL, NULL,
4152 NULL /* have_base */,
4153 NULL /* have_more_work */,
4154 NULL /* have_work */,
4155 src_wcroot, local_relpath, result_pool, scratch_pool));
4158 *op_root = is_op_root;
4160 if (node_status == svn_wc__db_status_excluded)
4162 /* The parent cannot be excluded, so look at the parent and then
4163 adjust the relpath */
4164 const char *parent_relpath, *base_name;
4166 svn_dirent_split(&parent_relpath, &base_name, local_relpath,
4168 SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev,
4170 src_wcroot, parent_relpath, dst_wcroot,
4171 scratch_pool, scratch_pool));
4172 if (*copyfrom_relpath)
4173 *copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, base_name,
4176 else if (node_status == svn_wc__db_status_added)
4178 SVN_ERR(scan_addition(&node_status, NULL, NULL, NULL, NULL, NULL, NULL,
4179 NULL, NULL, NULL, src_wcroot, local_relpath,
4180 scratch_pool, scratch_pool));
4182 else if (node_status == svn_wc__db_status_deleted && is_op_root)
4184 const char *base_del_relpath, *work_del_relpath;
4186 SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
4188 NULL, src_wcroot, local_relpath,
4189 scratch_pool, scratch_pool));
4190 if (work_del_relpath)
4192 const char *op_root_relpath;
4193 const char *parent_del_relpath = svn_relpath_dirname(work_del_relpath,
4196 /* Similar to, but not the same as, the _scan_addition and
4197 _join above. Can we use get_copyfrom here? */
4198 SVN_ERR(scan_addition(NULL, &op_root_relpath,
4199 NULL, NULL, /* repos_* */
4200 copyfrom_relpath, copyfrom_id, copyfrom_rev,
4202 src_wcroot, parent_del_relpath,
4203 scratch_pool, scratch_pool));
4205 = svn_relpath_join(*copyfrom_relpath,
4206 svn_relpath_skip_ancestor(op_root_relpath,
4210 else if (base_del_relpath)
4212 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, copyfrom_rev,
4214 copyfrom_id, NULL, NULL,
4215 NULL, NULL, NULL, NULL,
4216 NULL, NULL, NULL, NULL,
4217 src_wcroot, local_relpath,
4222 SVN_ERR_MALFUNCTION();
4224 else if (node_status == svn_wc__db_status_deleted)
4226 /* Keep original_* from read_info() to allow seeing the difference
4227 between base-deleted and not present */
4231 *copyfrom_relpath = repos_relpath;
4232 *copyfrom_rev = revision;
4233 *copyfrom_id = repos_id;
4237 *status = node_status;
4239 if (src_wcroot != dst_wcroot && *copyfrom_relpath)
4241 const char *repos_root_url;
4242 const char *repos_uuid;
4244 /* Pass the right repos-id for the destination db. We can't just use
4245 the id of the source database, as this value can change after
4246 relocation (and perhaps also when we start storing multiple
4247 working copies in a single db)! */
4249 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
4250 src_wcroot->sdb, *copyfrom_id,
4253 SVN_ERR(create_repos_id(copyfrom_id, repos_root_url, repos_uuid,
4254 dst_wcroot->sdb, scratch_pool));
4257 return SVN_NO_ERROR;
4261 /* Set *OP_DEPTH to the highest op depth of WCROOT:LOCAL_RELPATH. */
4262 static svn_error_t *
4263 op_depth_of(int *op_depth,
4264 svn_wc__db_wcroot_t *wcroot,
4265 const char *local_relpath)
4267 svn_sqlite__stmt_t *stmt;
4268 svn_boolean_t have_row;
4270 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4271 STMT_SELECT_NODE_INFO));
4272 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4273 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4274 SVN_ERR_ASSERT(have_row);
4275 *op_depth = svn_sqlite__column_int(stmt, 0);
4276 SVN_ERR(svn_sqlite__reset(stmt));
4278 return SVN_NO_ERROR;
4282 /* Determine at which OP_DEPTH a copy of COPYFROM_REPOS_ID, COPYFROM_RELPATH at
4283 revision COPYFROM_REVISION should be inserted as LOCAL_RELPATH. Do this
4284 by checking if this would be a direct child of a copy of its parent
4285 directory. If it is then set *OP_DEPTH to the op_depth of its parent.
4287 If the node is not a direct copy at the same revision of the parent
4288 *NP_OP_DEPTH will be set to the op_depth of the parent when a not-present
4289 node should be inserted at this op_depth. This will be the case when the
4290 parent already defined an incomplete child with the same name. Otherwise
4291 *NP_OP_DEPTH will be set to -1.
4293 If the parent node is not the parent of the to be copied node, then
4294 *OP_DEPTH will be set to the proper op_depth for a new operation root.
4296 Set *PARENT_OP_DEPTH to the op_depth of the parent.
4299 static svn_error_t *
4300 op_depth_for_copy(int *op_depth,
4302 int *parent_op_depth,
4303 apr_int64_t copyfrom_repos_id,
4304 const char *copyfrom_relpath,
4305 svn_revnum_t copyfrom_revision,
4306 svn_wc__db_wcroot_t *wcroot,
4307 const char *local_relpath,
4308 apr_pool_t *scratch_pool)
4310 const char *parent_relpath, *name;
4311 svn_sqlite__stmt_t *stmt;
4312 svn_boolean_t have_row;
4313 int incomplete_op_depth = -1;
4314 int min_op_depth = 1; /* Never touch BASE */
4316 *op_depth = relpath_depth(local_relpath);
4319 svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool);
4320 *parent_op_depth = relpath_depth(parent_relpath);
4322 if (!copyfrom_relpath)
4323 return SVN_NO_ERROR;
4325 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4326 STMT_SELECT_WORKING_NODE));
4327 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4328 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4331 svn_wc__db_status_t status = svn_sqlite__column_token(stmt, 1,
4334 min_op_depth = svn_sqlite__column_int(stmt, 0);
4335 if (status == svn_wc__db_status_incomplete)
4336 incomplete_op_depth = min_op_depth;
4338 SVN_ERR(svn_sqlite__reset(stmt));
4340 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4341 STMT_SELECT_WORKING_NODE));
4342 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4343 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4346 svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 1,
4349 *parent_op_depth = svn_sqlite__column_int(stmt, 0);
4350 if (*parent_op_depth < min_op_depth)
4352 /* We want to create a copy; not overwrite the lower layers */
4353 SVN_ERR(svn_sqlite__reset(stmt));
4354 return SVN_NO_ERROR;
4357 /* You can only add children below a node that exists.
4358 In WORKING that must be status added, which is represented
4359 as presence normal */
4360 SVN_ERR_ASSERT(presence == svn_wc__db_status_normal);
4362 if ((incomplete_op_depth < 0)
4363 || (incomplete_op_depth == *parent_op_depth))
4365 apr_int64_t parent_copyfrom_repos_id
4366 = svn_sqlite__column_int64(stmt, 10);
4367 const char *parent_copyfrom_relpath
4368 = svn_sqlite__column_text(stmt, 11, NULL);
4369 svn_revnum_t parent_copyfrom_revision
4370 = svn_sqlite__column_revnum(stmt, 12);
4372 if (parent_copyfrom_repos_id == copyfrom_repos_id)
4374 if (copyfrom_revision == parent_copyfrom_revision
4375 && !strcmp(copyfrom_relpath,
4376 svn_relpath_join(parent_copyfrom_relpath, name,
4378 *op_depth = *parent_op_depth;
4379 else if (incomplete_op_depth > 0)
4380 *np_op_depth = incomplete_op_depth;
4384 SVN_ERR(svn_sqlite__reset(stmt));
4386 return SVN_NO_ERROR;
4390 /* Like svn_wc__db_op_copy(), but with WCROOT+LOCAL_RELPATH
4391 * instead of DB+LOCAL_ABSPATH. A non-zero MOVE_OP_DEPTH implies that the
4392 * copy operation is part of a move, and indicates the op-depth of the
4393 * move destination op-root. */
4394 static svn_error_t *
4395 db_op_copy(svn_wc__db_wcroot_t *src_wcroot,
4396 const char *src_relpath,
4397 svn_wc__db_wcroot_t *dst_wcroot,
4398 const char *dst_relpath,
4399 const svn_skel_t *work_items,
4401 apr_pool_t *scratch_pool)
4403 const char *copyfrom_relpath;
4404 svn_revnum_t copyfrom_rev;
4405 svn_wc__db_status_t status;
4406 svn_wc__db_status_t dst_presence;
4407 svn_boolean_t op_root;
4408 apr_int64_t copyfrom_id;
4410 int dst_np_op_depth;
4411 int dst_parent_op_depth;
4412 svn_node_kind_t kind;
4413 const apr_array_header_t *children;
4415 SVN_ERR(get_info_for_copy(©from_id, ©from_relpath, ©from_rev,
4416 &status, &kind, &op_root,
4417 src_wcroot, src_relpath, dst_wcroot,
4418 scratch_pool, scratch_pool));
4420 SVN_ERR(op_depth_for_copy(&dst_op_depth, &dst_np_op_depth,
4421 &dst_parent_op_depth,
4422 copyfrom_id, copyfrom_relpath, copyfrom_rev,
4423 dst_wcroot, dst_relpath, scratch_pool));
4425 SVN_ERR_ASSERT(kind == svn_node_file || kind == svn_node_dir);
4427 /* ### New status, not finished, see notes/wc-ng/copying */
4430 case svn_wc__db_status_normal:
4431 case svn_wc__db_status_added:
4432 case svn_wc__db_status_moved_here:
4433 case svn_wc__db_status_copied:
4434 dst_presence = svn_wc__db_status_normal;
4436 case svn_wc__db_status_deleted:
4439 /* If the lower layer is already shadowcopied we can skip adding
4440 a not present node. */
4442 svn_wc__db_status_t dst_status;
4444 err = read_info(&dst_status, NULL, NULL, NULL, NULL, NULL, NULL,
4445 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4446 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4447 dst_wcroot, dst_relpath, scratch_pool, scratch_pool);
4451 if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
4452 svn_error_clear(err);
4454 return svn_error_trace(err);
4456 else if (dst_status == svn_wc__db_status_deleted)
4458 /* Node is already deleted; skip the NODES work, but do
4459 install wq items if requested */
4460 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4462 return SVN_NO_ERROR;
4467 /* This node is either a not-present node (which should be copied), or
4468 a base-delete of some lower layer (which shouldn't).
4469 Subversion <= 1.7 always added a not-present node here, which is
4470 safe (as it postpones the hard work until commit time and then we
4471 ask the repository), but it breaks some move scenarios.
4474 if (! copyfrom_relpath)
4476 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4478 return SVN_NO_ERROR;
4481 /* Fall through. Install not present node */
4483 case svn_wc__db_status_not_present:
4484 case svn_wc__db_status_excluded:
4485 /* These presence values should not create a new op depth */
4486 if (dst_np_op_depth > 0)
4488 dst_op_depth = dst_np_op_depth;
4489 dst_np_op_depth = -1;
4491 if (status == svn_wc__db_status_excluded)
4492 dst_presence = svn_wc__db_status_excluded;
4494 dst_presence = svn_wc__db_status_not_present;
4496 case svn_wc__db_status_server_excluded:
4497 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4498 _("Cannot copy '%s' excluded by server"),
4499 path_for_error_message(src_wcroot,
4503 /* Perhaps we should allow incomplete to incomplete? We can't
4504 avoid incomplete working nodes as one step in copying a
4505 directory is to add incomplete children. */
4506 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4507 _("Cannot handle status of '%s'"),
4508 path_for_error_message(src_wcroot,
4513 if (kind == svn_node_dir)
4517 SVN_ERR(op_depth_of(&src_op_depth, src_wcroot, src_relpath));
4518 SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
4519 src_op_depth, scratch_pool, scratch_pool));
4524 if (src_wcroot == dst_wcroot)
4526 svn_sqlite__stmt_t *stmt;
4527 const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
4530 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
4531 STMT_INSERT_WORKING_NODE_COPY_FROM));
4533 SVN_ERR(svn_sqlite__bindf(stmt, "issdst",
4534 src_wcroot->wc_id, src_relpath,
4538 presence_map, dst_presence));
4540 if (move_op_depth > 0)
4542 if (relpath_depth(dst_relpath) == move_op_depth)
4544 /* We're moving the root of the move operation.
4546 * When an added node or the op-root of a copy is moved,
4547 * there is no 'moved-from' corresponding to the moved-here
4548 * node. So the net effect is the same as copy+delete.
4549 * Perform a normal copy operation in these cases. */
4550 if (!(status == svn_wc__db_status_added ||
4551 (status == svn_wc__db_status_copied && op_root)))
4552 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4556 svn_sqlite__stmt_t *info_stmt;
4557 svn_boolean_t have_row;
4559 /* We're moving a child along with the root of the move.
4561 * Set moved-here depending on dst_parent, propagating the
4562 * above decision to moved-along children at the same op_depth.
4563 * We can't use scan_addition() to detect moved-here because
4564 * the delete-half of the move might not yet exist. */
4565 SVN_ERR(svn_sqlite__get_statement(&info_stmt, dst_wcroot->sdb,
4566 STMT_SELECT_NODE_INFO));
4567 SVN_ERR(svn_sqlite__bindf(info_stmt, "is", dst_wcroot->wc_id,
4568 dst_parent_relpath));
4569 SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4570 SVN_ERR_ASSERT(have_row);
4571 if (svn_sqlite__column_boolean(info_stmt, 15) &&
4572 dst_op_depth == dst_parent_op_depth)
4574 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4575 SVN_ERR(svn_sqlite__reset(info_stmt));
4579 SVN_ERR(svn_sqlite__reset(info_stmt));
4581 /* If the child has been moved into the tree we're moving,
4582 * keep its moved-here bit set. */
4583 SVN_ERR(svn_sqlite__get_statement(&info_stmt,
4585 STMT_SELECT_NODE_INFO));
4586 SVN_ERR(svn_sqlite__bindf(info_stmt, "is",
4587 dst_wcroot->wc_id, src_relpath));
4588 SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4589 SVN_ERR_ASSERT(have_row);
4590 if (svn_sqlite__column_boolean(info_stmt, 15))
4591 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4592 SVN_ERR(svn_sqlite__reset(info_stmt));
4597 SVN_ERR(svn_sqlite__step_done(stmt));
4599 /* ### Copying changelist is OK for a move but what about a copy? */
4600 SVN_ERR(copy_actual(src_wcroot, src_relpath,
4601 dst_wcroot, dst_relpath, scratch_pool));
4603 if (dst_np_op_depth > 0)
4605 /* We introduce a not-present node at the parent's op_depth to
4606 properly start a new op-depth at our own op_depth. This marks
4607 us as an op_root for commit and allows reverting just this
4610 SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
4612 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
4613 src_wcroot->wc_id, dst_relpath,
4614 dst_np_op_depth, dst_parent_relpath,
4615 copyfrom_id, copyfrom_relpath,
4618 svn_wc__db_status_not_present,
4622 SVN_ERR(svn_sqlite__step_done(stmt));
4624 /* Insert incomplete children, if relevant.
4625 The children are part of the same op and so have the same op_depth.
4626 (The only time we'd want a different depth is during a recursive
4627 simple add, but we never insert children here during a simple add.) */
4628 if (kind == svn_node_dir
4629 && dst_presence == svn_wc__db_status_normal)
4630 SVN_ERR(insert_incomplete_children(
4643 SVN_ERR(cross_db_copy(src_wcroot, src_relpath, dst_wcroot,
4644 dst_relpath, dst_presence, dst_op_depth,
4645 dst_np_op_depth, kind,
4646 children, copyfrom_id, copyfrom_relpath,
4647 copyfrom_rev, scratch_pool));
4650 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items, scratch_pool));
4652 return SVN_NO_ERROR;
4655 /* Baton for passing args to op_copy_txn(). */
4656 struct op_copy_baton
4658 svn_wc__db_wcroot_t *src_wcroot;
4659 const char *src_relpath;
4661 svn_wc__db_wcroot_t *dst_wcroot;
4662 const char *dst_relpath;
4664 const svn_skel_t *work_items;
4666 svn_boolean_t is_move;
4667 const char *dst_op_root_relpath;
4670 /* Helper for svn_wc__db_op_copy().
4672 * Implements svn_sqlite__transaction_callback_t. */
4673 static svn_error_t *
4674 op_copy_txn(void * baton,
4675 svn_sqlite__db_t *sdb,
4676 apr_pool_t *scratch_pool)
4678 struct op_copy_baton *ocb = baton;
4681 if (sdb != ocb->dst_wcroot->sdb)
4683 /* Source and destination databases differ; so also start a lock
4684 in the destination database, by calling ourself in a lock. */
4686 return svn_error_trace(
4687 svn_sqlite__with_lock(ocb->dst_wcroot->sdb,
4688 op_copy_txn, ocb, scratch_pool));
4691 /* From this point we can assume a lock in the src and dst databases */
4694 move_op_depth = relpath_depth(ocb->dst_op_root_relpath);
4698 SVN_ERR(db_op_copy(ocb->src_wcroot, ocb->src_relpath,
4699 ocb->dst_wcroot, ocb->dst_relpath,
4700 ocb->work_items, move_op_depth, scratch_pool));
4702 return SVN_NO_ERROR;
4706 svn_wc__db_op_copy(svn_wc__db_t *db,
4707 const char *src_abspath,
4708 const char *dst_abspath,
4709 const char *dst_op_root_abspath,
4710 svn_boolean_t is_move,
4711 const svn_skel_t *work_items,
4712 apr_pool_t *scratch_pool)
4714 struct op_copy_baton ocb = {0};
4716 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
4717 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
4718 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_op_root_abspath));
4720 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
4721 &ocb.src_relpath, db,
4723 scratch_pool, scratch_pool));
4724 VERIFY_USABLE_WCROOT(ocb.src_wcroot);
4726 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
4729 scratch_pool, scratch_pool));
4730 VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
4732 ocb.work_items = work_items;
4733 ocb.is_move = is_move;
4734 ocb.dst_op_root_relpath = svn_dirent_skip_ancestor(ocb.dst_wcroot->abspath,
4735 dst_op_root_abspath);
4737 /* Call with the sdb in src_wcroot. It might call itself again to
4738 also obtain a lock in dst_wcroot */
4739 SVN_ERR(svn_sqlite__with_lock(ocb.src_wcroot->sdb, op_copy_txn, &ocb,
4742 return SVN_NO_ERROR;
4745 /* The txn body of svn_wc__db_op_handle_move_back */
4746 static svn_error_t *
4747 handle_move_back(svn_boolean_t *moved_back,
4748 svn_wc__db_wcroot_t *wcroot,
4749 const char *local_relpath,
4750 const char *moved_from_relpath,
4751 const svn_skel_t *work_items,
4752 apr_pool_t *scratch_pool)
4754 svn_sqlite__stmt_t *stmt;
4755 svn_wc__db_status_t status;
4756 svn_boolean_t op_root;
4757 svn_boolean_t have_more_work;
4758 int from_op_depth = 0;
4759 svn_boolean_t have_row;
4760 svn_boolean_t different = FALSE;
4762 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
4764 SVN_ERR(svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL, NULL,
4765 NULL, NULL, NULL, NULL, NULL, NULL,
4766 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4767 &op_root, NULL, NULL, NULL,
4768 &have_more_work, NULL,
4769 wcroot, local_relpath,
4770 scratch_pool, scratch_pool));
4772 if (status != svn_wc__db_status_added || !op_root)
4773 return SVN_NO_ERROR;
4775 /* We have two cases here: BASE-move-back and WORKING-move-back */
4777 SVN_ERR(op_depth_of(&from_op_depth, wcroot,
4778 svn_relpath_dirname(local_relpath, scratch_pool)));
4782 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4783 STMT_SELECT_MOVED_BACK));
4785 SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id,
4788 relpath_depth(local_relpath)));
4790 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4792 SVN_ERR_ASSERT(have_row); /* We checked that the node is an op-root */
4795 svn_boolean_t moved_here = svn_sqlite__column_boolean(stmt, 9);
4796 const char *moved_to = svn_sqlite__column_text(stmt, 10, NULL);
4800 || strcmp(moved_to, moved_from_relpath))
4809 svn_wc__db_status_t upper_status;
4810 svn_wc__db_status_t lower_status;
4812 upper_status = svn_sqlite__column_token(stmt, 1, presence_map);
4814 if (svn_sqlite__column_is_null(stmt, 5))
4816 /* No lower layer replaced. */
4817 if (upper_status != svn_wc__db_status_not_present)
4825 lower_status = svn_sqlite__column_token(stmt, 5, presence_map);
4827 if (upper_status != lower_status)
4833 if (upper_status == svn_wc__db_status_not_present
4834 || upper_status == svn_wc__db_status_excluded)
4836 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4837 continue; /* Nothing to check */
4839 else if (upper_status != svn_wc__db_status_normal)
4841 /* Not a normal move. Mixed revision move? */
4847 const char *upper_repos_relpath;
4848 const char *lower_repos_relpath;
4850 upper_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL);
4851 lower_repos_relpath = svn_sqlite__column_text(stmt, 7, NULL);
4853 if (! upper_repos_relpath
4854 || strcmp(upper_repos_relpath, lower_repos_relpath))
4862 svn_revnum_t upper_rev;
4863 svn_revnum_t lower_rev;
4865 upper_rev = svn_sqlite__column_revnum(stmt, 4);
4866 lower_rev = svn_sqlite__column_revnum(stmt, 8);
4868 if (upper_rev != lower_rev)
4876 apr_int64_t upper_repos_id;
4877 apr_int64_t lower_repos_id;
4879 upper_repos_id = svn_sqlite__column_int64(stmt, 2);
4880 lower_repos_id = svn_sqlite__column_int64(stmt, 6);
4882 if (upper_repos_id != lower_repos_id)
4889 /* Check moved_here? */
4891 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4893 SVN_ERR(svn_sqlite__reset(stmt));
4897 /* Ok, we can now safely remove this complete move, because we
4898 determined that it 100% matches the layer below it. */
4900 /* ### We could copy the recorded timestamps from the higher to the
4901 lower layer in an attempt to improve status performance, but
4902 generally these values should be the same anyway as it was
4904 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4905 STMT_DELETE_MOVED_BACK));
4907 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
4909 relpath_depth(local_relpath)));
4911 SVN_ERR(svn_sqlite__step_done(stmt));
4917 return SVN_NO_ERROR;
4921 svn_wc__db_op_handle_move_back(svn_boolean_t *moved_back,
4923 const char *local_abspath,
4924 const char *moved_from_abspath,
4925 const svn_skel_t *work_items,
4926 apr_pool_t *scratch_pool)
4928 svn_wc__db_wcroot_t *wcroot;
4929 const char *local_relpath;
4930 const char *moved_from_relpath;
4931 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4933 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4935 scratch_pool, scratch_pool));
4936 VERIFY_USABLE_WCROOT(wcroot);
4939 *moved_back = FALSE;
4941 moved_from_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
4942 moved_from_abspath);
4944 if (! local_relpath[0]
4945 || !moved_from_relpath)
4947 /* WC-Roots can't be moved */
4948 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
4949 return SVN_NO_ERROR;
4952 SVN_WC__DB_WITH_TXN(handle_move_back(moved_back, wcroot, local_relpath,
4953 moved_from_relpath, work_items,
4957 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
4960 return SVN_NO_ERROR;
4964 /* The recursive implementation of svn_wc__db_op_copy_shadowed_layer.
4966 * A non-zero MOVE_OP_DEPTH implies that the copy operation is part of
4967 * a move, and indicates the op-depth of the move destination op-root. */
4968 static svn_error_t *
4969 db_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot,
4970 const char *src_relpath,
4972 svn_wc__db_wcroot_t *dst_wcroot,
4973 const char *dst_relpath,
4976 apr_int64_t repos_id,
4977 const char *repos_relpath,
4978 svn_revnum_t revision,
4980 apr_pool_t *scratch_pool)
4982 const apr_array_header_t *children;
4983 apr_pool_t *iterpool;
4984 svn_wc__db_status_t status;
4985 svn_node_kind_t kind;
4986 svn_revnum_t node_revision;
4987 const char *node_repos_relpath;
4988 apr_int64_t node_repos_id;
4989 svn_sqlite__stmt_t *stmt;
4990 svn_wc__db_status_t dst_presence;
4995 err = svn_wc__db_depth_get_info(&status, &kind, &node_revision,
4996 &node_repos_relpath, &node_repos_id,
4997 NULL, NULL, NULL, NULL, NULL, NULL,
4999 src_wcroot, src_relpath, src_op_depth,
5000 scratch_pool, scratch_pool);
5004 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5005 return svn_error_trace(err);
5007 svn_error_clear(err);
5008 return SVN_NO_ERROR; /* There is no shadowed node at src_op_depth */
5012 if (src_op_depth == 0)
5014 /* If the node is switched or has a different revision then its parent
5015 we shouldn't copy it. (We can't as we would have to insert it at
5016 an unshadowed depth) */
5017 if (status == svn_wc__db_status_not_present
5018 || status == svn_wc__db_status_excluded
5019 || status == svn_wc__db_status_server_excluded
5020 || node_revision != revision
5021 || node_repos_id != repos_id
5022 || strcmp(node_repos_relpath, repos_relpath))
5024 /* Add a not-present node in the destination wcroot */
5025 struct insert_working_baton_t iwb;
5026 const char *repos_root_url;
5027 const char *repos_uuid;
5029 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
5030 src_wcroot->sdb, node_repos_id,
5033 SVN_ERR(create_repos_id(&node_repos_id, repos_root_url, repos_uuid,
5034 dst_wcroot->sdb, scratch_pool));
5038 iwb.op_depth = dst_op_depth;
5039 if (status != svn_wc__db_status_excluded)
5040 iwb.presence = svn_wc__db_status_not_present;
5042 iwb.presence = svn_wc__db_status_excluded;
5046 iwb.original_repos_id = node_repos_id;
5047 iwb.original_revnum = node_revision;
5048 iwb.original_repos_relpath = node_repos_relpath;
5050 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5053 return SVN_NO_ERROR;
5057 iterpool = svn_pool_create(scratch_pool);
5061 case svn_wc__db_status_normal:
5062 case svn_wc__db_status_added:
5063 case svn_wc__db_status_moved_here:
5064 case svn_wc__db_status_copied:
5065 dst_presence = svn_wc__db_status_normal;
5067 case svn_wc__db_status_deleted:
5068 case svn_wc__db_status_not_present:
5069 dst_presence = svn_wc__db_status_not_present;
5071 case svn_wc__db_status_excluded:
5072 dst_presence = svn_wc__db_status_excluded;
5074 case svn_wc__db_status_server_excluded:
5075 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5076 _("Cannot copy '%s' excluded by server"),
5077 path_for_error_message(src_wcroot,
5081 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5082 _("Cannot handle status of '%s'"),
5083 path_for_error_message(src_wcroot,
5088 if (dst_presence == svn_wc__db_status_normal
5089 && src_wcroot == dst_wcroot) /* ### Remove limitation */
5091 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
5092 STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH));
5094 SVN_ERR(svn_sqlite__bindf(stmt, "issdstd",
5095 src_wcroot->wc_id, src_relpath,
5098 svn_relpath_dirname(dst_relpath, iterpool),
5099 presence_map, dst_presence,
5103 if (dst_op_depth == move_op_depth)
5104 SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
5106 SVN_ERR(svn_sqlite__step_done(stmt));
5109 /* And mark it deleted to allow proper shadowing */
5110 struct insert_working_baton_t iwb;
5114 iwb.op_depth = del_op_depth;
5115 iwb.presence = svn_wc__db_status_base_deleted;
5119 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5125 struct insert_working_baton_t iwb;
5126 if (dst_presence == svn_wc__db_status_normal) /* Fallback for multi-db */
5127 dst_presence = svn_wc__db_status_not_present;
5129 /* And mark it deleted to allow proper shadowing */
5133 iwb.op_depth = dst_op_depth;
5134 iwb.presence = dst_presence;
5137 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5141 if (dst_presence == svn_wc__db_status_not_present)
5143 /* Don't create descendants of a not present node! */
5145 /* This code is currently still triggered by copying deleted nodes
5146 between separate working copies. See ### comment above. */
5148 svn_pool_destroy(iterpool);
5149 return SVN_NO_ERROR;
5152 SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
5153 src_op_depth, scratch_pool, iterpool));
5155 for (i = 0; i < children->nelts; i++)
5157 const char *name = APR_ARRAY_IDX(children, i, const char *);
5158 const char *child_src_relpath;
5159 const char *child_dst_relpath;
5160 const char *child_repos_relpath = NULL;
5162 svn_pool_clear(iterpool);
5163 child_src_relpath = svn_relpath_join(src_relpath, name, iterpool);
5164 child_dst_relpath = svn_relpath_join(dst_relpath, name, iterpool);
5167 child_repos_relpath = svn_relpath_join(repos_relpath, name, iterpool);
5169 SVN_ERR(db_op_copy_shadowed_layer(
5170 src_wcroot, child_src_relpath, src_op_depth,
5171 dst_wcroot, child_dst_relpath, dst_op_depth,
5173 repos_id, child_repos_relpath, revision,
5174 move_op_depth, scratch_pool));
5177 svn_pool_destroy(iterpool);
5179 return SVN_NO_ERROR;
5182 /* Helper for svn_wc__db_op_copy_shadowed_layer().
5184 * Implements svn_sqlite__transaction_callback_t. */
5185 static svn_error_t *
5186 op_copy_shadowed_layer_txn(void *baton,
5187 svn_sqlite__db_t *sdb,
5188 apr_pool_t *scratch_pool)
5190 struct op_copy_baton *ocb = baton;
5191 const char *src_parent_relpath;
5192 const char *dst_parent_relpath;
5196 const char *repos_relpath = NULL;
5197 apr_int64_t repos_id = INVALID_REPOS_ID;
5198 svn_revnum_t revision = SVN_INVALID_REVNUM;
5200 if (sdb != ocb->dst_wcroot->sdb)
5202 /* Source and destination databases differ; so also start a lock
5203 in the destination database, by calling ourself in a lock. */
5205 return svn_error_trace(
5206 svn_sqlite__with_lock(ocb->dst_wcroot->sdb,
5207 op_copy_shadowed_layer_txn,
5208 ocb, scratch_pool));
5211 /* From this point we can assume a lock in the src and dst databases */
5214 /* src_relpath and dst_relpath can't be wcroot as we need their parents */
5215 SVN_ERR_ASSERT(*ocb->src_relpath && *ocb->dst_relpath);
5217 src_parent_relpath = svn_relpath_dirname(ocb->src_relpath, scratch_pool);
5218 dst_parent_relpath = svn_relpath_dirname(ocb->dst_relpath, scratch_pool);
5220 /* src_parent must be status normal or added; get its op-depth */
5221 SVN_ERR(op_depth_of(&src_op_depth, ocb->src_wcroot, src_parent_relpath));
5223 /* dst_parent must be status added; get its op-depth */
5224 SVN_ERR(op_depth_of(&dst_op_depth, ocb->dst_wcroot, dst_parent_relpath));
5226 del_op_depth = relpath_depth(ocb->dst_relpath);
5228 /* Get some information from the parent */
5229 SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, &revision, &repos_relpath,
5230 &repos_id, NULL, NULL, NULL, NULL, NULL,
5233 src_parent_relpath, src_op_depth,
5234 scratch_pool, scratch_pool));
5236 if (repos_relpath == NULL)
5238 /* The node is a local addition and has no shadowed information */
5239 return SVN_NO_ERROR;
5242 /* And calculate the child repos relpath */
5243 repos_relpath = svn_relpath_join(repos_relpath,
5244 svn_relpath_basename(ocb->src_relpath,
5248 SVN_ERR(db_op_copy_shadowed_layer(
5249 ocb->src_wcroot, ocb->src_relpath, src_op_depth,
5250 ocb->dst_wcroot, ocb->dst_relpath, dst_op_depth,
5252 repos_id, repos_relpath, revision,
5253 (ocb->is_move ? dst_op_depth : 0),
5256 return SVN_NO_ERROR;
5260 svn_wc__db_op_copy_shadowed_layer(svn_wc__db_t *db,
5261 const char *src_abspath,
5262 const char *dst_abspath,
5263 svn_boolean_t is_move,
5264 apr_pool_t *scratch_pool)
5266 struct op_copy_baton ocb = {0};
5268 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
5269 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
5271 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
5272 &ocb.src_relpath, db,
5274 scratch_pool, scratch_pool));
5275 VERIFY_USABLE_WCROOT(ocb.src_wcroot);
5277 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
5280 scratch_pool, scratch_pool));
5281 VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
5283 ocb.is_move = is_move;
5284 ocb.dst_op_root_relpath = NULL; /* not used by op_copy_shadowed_layer_txn */
5286 ocb.work_items = NULL;
5288 /* Call with the sdb in src_wcroot. It might call itself again to
5289 also obtain a lock in dst_wcroot */
5290 SVN_ERR(svn_sqlite__with_lock(ocb.src_wcroot->sdb,
5291 op_copy_shadowed_layer_txn,
5292 &ocb, scratch_pool));
5294 return SVN_NO_ERROR;
5298 /* If there are any server-excluded base nodes then the copy must fail
5299 as it's not possible to commit such a copy.
5300 Return an error if there are any server-excluded nodes. */
5301 static svn_error_t *
5302 catch_copy_of_server_excluded(svn_wc__db_wcroot_t *wcroot,
5303 const char *local_relpath,
5304 apr_pool_t *scratch_pool)
5306 svn_sqlite__stmt_t *stmt;
5307 svn_boolean_t have_row;
5308 const char *server_excluded_relpath;
5310 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5311 STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
5312 SVN_ERR(svn_sqlite__bindf(stmt, "is",
5315 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5317 server_excluded_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
5318 SVN_ERR(svn_sqlite__reset(stmt));
5320 return svn_error_createf(SVN_ERR_AUTHZ_UNREADABLE, NULL,
5321 _("Cannot copy '%s' excluded by server"),
5322 path_for_error_message(wcroot,
5323 server_excluded_relpath,
5326 return SVN_NO_ERROR;
5331 svn_wc__db_op_copy_dir(svn_wc__db_t *db,
5332 const char *local_abspath,
5333 const apr_hash_t *props,
5334 svn_revnum_t changed_rev,
5335 apr_time_t changed_date,
5336 const char *changed_author,
5337 const char *original_repos_relpath,
5338 const char *original_root_url,
5339 const char *original_uuid,
5340 svn_revnum_t original_revision,
5341 const apr_array_header_t *children,
5342 svn_boolean_t is_move,
5344 const svn_skel_t *conflict,
5345 const svn_skel_t *work_items,
5346 apr_pool_t *scratch_pool)
5348 svn_wc__db_wcroot_t *wcroot;
5349 const char *local_relpath;
5350 insert_working_baton_t iwb;
5351 int parent_op_depth;
5353 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5354 SVN_ERR_ASSERT(props != NULL);
5355 /* ### any assertions for CHANGED_* ? */
5356 /* ### any assertions for ORIGINAL_* ? */
5358 SVN_ERR_ASSERT(children != NULL);
5361 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5362 local_abspath, scratch_pool, scratch_pool));
5363 VERIFY_USABLE_WCROOT(wcroot);
5367 iwb.presence = svn_wc__db_status_normal;
5368 iwb.kind = svn_node_dir;
5371 iwb.changed_rev = changed_rev;
5372 iwb.changed_date = changed_date;
5373 iwb.changed_author = changed_author;
5375 if (original_root_url != NULL)
5377 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5378 original_root_url, original_uuid,
5379 wcroot->sdb, scratch_pool));
5380 iwb.original_repos_relpath = original_repos_relpath;
5381 iwb.original_revnum = original_revision;
5384 /* ### Should we do this inside the transaction? */
5385 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5386 &parent_op_depth, iwb.original_repos_id,
5387 original_repos_relpath, original_revision,
5388 wcroot, local_relpath, scratch_pool));
5390 iwb.children = children;
5392 iwb.moved_here = is_move && (parent_op_depth == 0 ||
5393 iwb.op_depth == parent_op_depth);
5395 iwb.work_items = work_items;
5396 iwb.conflict = conflict;
5398 SVN_WC__DB_WITH_TXN(
5399 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5401 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
5403 return SVN_NO_ERROR;
5408 svn_wc__db_op_copy_file(svn_wc__db_t *db,
5409 const char *local_abspath,
5410 const apr_hash_t *props,
5411 svn_revnum_t changed_rev,
5412 apr_time_t changed_date,
5413 const char *changed_author,
5414 const char *original_repos_relpath,
5415 const char *original_root_url,
5416 const char *original_uuid,
5417 svn_revnum_t original_revision,
5418 const svn_checksum_t *checksum,
5419 svn_boolean_t update_actual_props,
5420 const apr_hash_t *new_actual_props,
5421 svn_boolean_t is_move,
5422 const svn_skel_t *conflict,
5423 const svn_skel_t *work_items,
5424 apr_pool_t *scratch_pool)
5426 svn_wc__db_wcroot_t *wcroot;
5427 const char *local_relpath;
5428 insert_working_baton_t iwb;
5429 int parent_op_depth;
5431 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5432 SVN_ERR_ASSERT(props != NULL);
5433 /* ### any assertions for CHANGED_* ? */
5434 SVN_ERR_ASSERT((! original_repos_relpath && ! original_root_url
5435 && ! original_uuid && ! checksum
5436 && original_revision == SVN_INVALID_REVNUM)
5437 || (original_repos_relpath && original_root_url
5438 && original_uuid && checksum
5439 && original_revision != SVN_INVALID_REVNUM));
5441 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5442 local_abspath, scratch_pool, scratch_pool));
5443 VERIFY_USABLE_WCROOT(wcroot);
5447 iwb.presence = svn_wc__db_status_normal;
5448 iwb.kind = svn_node_file;
5451 iwb.changed_rev = changed_rev;
5452 iwb.changed_date = changed_date;
5453 iwb.changed_author = changed_author;
5455 if (original_root_url != NULL)
5457 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5458 original_root_url, original_uuid,
5459 wcroot->sdb, scratch_pool));
5460 iwb.original_repos_relpath = original_repos_relpath;
5461 iwb.original_revnum = original_revision;
5464 /* ### Should we do this inside the transaction? */
5465 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5466 &parent_op_depth, iwb.original_repos_id,
5467 original_repos_relpath, original_revision,
5468 wcroot, local_relpath, scratch_pool));
5470 iwb.checksum = checksum;
5471 iwb.moved_here = is_move && (parent_op_depth == 0 ||
5472 iwb.op_depth == parent_op_depth);
5474 if (update_actual_props)
5476 iwb.update_actual_props = update_actual_props;
5477 iwb.new_actual_props = new_actual_props;
5480 iwb.work_items = work_items;
5481 iwb.conflict = conflict;
5483 SVN_WC__DB_WITH_TXN(
5484 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5486 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5488 return SVN_NO_ERROR;
5493 svn_wc__db_op_copy_symlink(svn_wc__db_t *db,
5494 const char *local_abspath,
5495 const apr_hash_t *props,
5496 svn_revnum_t changed_rev,
5497 apr_time_t changed_date,
5498 const char *changed_author,
5499 const char *original_repos_relpath,
5500 const char *original_root_url,
5501 const char *original_uuid,
5502 svn_revnum_t original_revision,
5504 const svn_skel_t *conflict,
5505 const svn_skel_t *work_items,
5506 apr_pool_t *scratch_pool)
5508 svn_wc__db_wcroot_t *wcroot;
5509 const char *local_relpath;
5510 insert_working_baton_t iwb;
5511 int parent_op_depth;
5513 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5514 SVN_ERR_ASSERT(props != NULL);
5515 /* ### any assertions for CHANGED_* ? */
5516 /* ### any assertions for ORIGINAL_* ? */
5517 SVN_ERR_ASSERT(target != NULL);
5519 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5520 local_abspath, scratch_pool, scratch_pool));
5521 VERIFY_USABLE_WCROOT(wcroot);
5525 iwb.presence = svn_wc__db_status_normal;
5526 iwb.kind = svn_node_symlink;
5529 iwb.changed_rev = changed_rev;
5530 iwb.changed_date = changed_date;
5531 iwb.changed_author = changed_author;
5532 iwb.moved_here = FALSE;
5534 if (original_root_url != NULL)
5536 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5537 original_root_url, original_uuid,
5538 wcroot->sdb, scratch_pool));
5539 iwb.original_repos_relpath = original_repos_relpath;
5540 iwb.original_revnum = original_revision;
5543 /* ### Should we do this inside the transaction? */
5544 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5545 &parent_op_depth, iwb.original_repos_id,
5546 original_repos_relpath, original_revision,
5547 wcroot, local_relpath, scratch_pool));
5549 iwb.target = target;
5551 iwb.work_items = work_items;
5552 iwb.conflict = conflict;
5554 SVN_WC__DB_WITH_TXN(
5555 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5557 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5559 return SVN_NO_ERROR;
5564 svn_wc__db_op_add_directory(svn_wc__db_t *db,
5565 const char *local_abspath,
5566 const apr_hash_t *props,
5567 const svn_skel_t *work_items,
5568 apr_pool_t *scratch_pool)
5570 svn_wc__db_wcroot_t *wcroot;
5571 const char *local_relpath;
5572 const char *dir_abspath;
5574 insert_working_baton_t iwb;
5576 /* Resolve wcroot via parent directory to avoid obstruction handling */
5577 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5578 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5580 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5581 dir_abspath, scratch_pool, scratch_pool));
5582 VERIFY_USABLE_WCROOT(wcroot);
5586 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5587 iwb.presence = svn_wc__db_status_normal;
5588 iwb.kind = svn_node_dir;
5589 iwb.op_depth = relpath_depth(local_relpath);
5590 if (props && apr_hash_count((apr_hash_t *)props))
5592 iwb.update_actual_props = TRUE;
5593 iwb.new_actual_props = props;
5596 iwb.work_items = work_items;
5598 SVN_WC__DB_WITH_TXN(
5599 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5601 /* Use depth infinity to make sure we have no invalid cached information
5602 * about children of this dir. */
5603 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5606 return SVN_NO_ERROR;
5611 svn_wc__db_op_add_file(svn_wc__db_t *db,
5612 const char *local_abspath,
5613 const apr_hash_t *props,
5614 const svn_skel_t *work_items,
5615 apr_pool_t *scratch_pool)
5617 svn_wc__db_wcroot_t *wcroot;
5618 const char *local_relpath;
5619 insert_working_baton_t iwb;
5620 const char *dir_abspath;
5623 /* Resolve wcroot via parent directory to avoid obstruction handling */
5624 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5625 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5627 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5628 dir_abspath, scratch_pool, scratch_pool));
5629 VERIFY_USABLE_WCROOT(wcroot);
5633 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5634 iwb.presence = svn_wc__db_status_normal;
5635 iwb.kind = svn_node_file;
5636 iwb.op_depth = relpath_depth(local_relpath);
5637 if (props && apr_hash_count((apr_hash_t *)props))
5639 iwb.update_actual_props = TRUE;
5640 iwb.new_actual_props = props;
5643 iwb.work_items = work_items;
5645 SVN_WC__DB_WITH_TXN(
5646 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5648 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5650 return SVN_NO_ERROR;
5655 svn_wc__db_op_add_symlink(svn_wc__db_t *db,
5656 const char *local_abspath,
5658 const apr_hash_t *props,
5659 const svn_skel_t *work_items,
5660 apr_pool_t *scratch_pool)
5662 svn_wc__db_wcroot_t *wcroot;
5663 const char *local_relpath;
5664 insert_working_baton_t iwb;
5665 const char *dir_abspath;
5668 /* Resolve wcroot via parent directory to avoid obstruction handling */
5669 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5670 SVN_ERR_ASSERT(target != NULL);
5672 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5674 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5675 dir_abspath, scratch_pool, scratch_pool));
5677 VERIFY_USABLE_WCROOT(wcroot);
5681 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5682 iwb.presence = svn_wc__db_status_normal;
5683 iwb.kind = svn_node_symlink;
5684 iwb.op_depth = relpath_depth(local_relpath);
5685 if (props && apr_hash_count((apr_hash_t *)props))
5687 iwb.update_actual_props = TRUE;
5688 iwb.new_actual_props = props;
5691 iwb.target = target;
5693 iwb.work_items = work_items;
5695 SVN_WC__DB_WITH_TXN(
5696 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5698 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5700 return SVN_NO_ERROR;
5703 /* Record RECORDED_SIZE and RECORDED_TIME into top layer in NODES */
5704 static svn_error_t *
5705 db_record_fileinfo(svn_wc__db_wcroot_t *wcroot,
5706 const char *local_relpath,
5707 apr_int64_t recorded_size,
5708 apr_int64_t recorded_time,
5709 apr_pool_t *scratch_pool)
5711 svn_sqlite__stmt_t *stmt;
5714 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5715 STMT_UPDATE_NODE_FILEINFO));
5716 SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
5717 recorded_size, recorded_time));
5718 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
5720 SVN_ERR_ASSERT(affected_rows == 1);
5722 return SVN_NO_ERROR;
5727 svn_wc__db_global_record_fileinfo(svn_wc__db_t *db,
5728 const char *local_abspath,
5729 svn_filesize_t recorded_size,
5730 apr_time_t recorded_time,
5731 apr_pool_t *scratch_pool)
5733 svn_wc__db_wcroot_t *wcroot;
5734 const char *local_relpath;
5736 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5738 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5739 local_abspath, scratch_pool, scratch_pool));
5740 VERIFY_USABLE_WCROOT(wcroot);
5742 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
5743 recorded_size, recorded_time, scratch_pool));
5745 /* We *totally* monkeyed the entries. Toss 'em. */
5746 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5748 return SVN_NO_ERROR;
5752 /* Set the ACTUAL_NODE properties column for (WC_ID, LOCAL_RELPATH) to
5755 * Note: PROPS=NULL means the actual props are the same as the pristine
5756 * props; to indicate no properties when the pristine has some props,
5757 * PROPS must be an empty hash. */
5758 static svn_error_t *
5759 set_actual_props(apr_int64_t wc_id,
5760 const char *local_relpath,
5762 svn_sqlite__db_t *db,
5763 apr_pool_t *scratch_pool)
5765 svn_sqlite__stmt_t *stmt;
5768 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_UPDATE_ACTUAL_PROPS));
5769 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
5770 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
5771 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
5773 if (affected_rows == 1 || !props)
5774 return SVN_NO_ERROR; /* We are done */
5776 /* We have to insert a row in ACTUAL */
5778 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_ACTUAL_PROPS));
5779 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
5780 if (*local_relpath != '\0')
5781 SVN_ERR(svn_sqlite__bind_text(stmt, 3,
5782 svn_relpath_dirname(local_relpath,
5784 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
5785 return svn_error_trace(svn_sqlite__step_done(stmt));
5789 /* The body of svn_wc__db_op_set_props().
5791 Set the 'properties' column in the 'ACTUAL_NODE' table to BATON->props.
5792 Create an entry in the ACTUAL table for the node if it does not yet
5794 To specify no properties, BATON->props must be an empty hash, not NULL.
5795 BATON is of type 'struct set_props_baton_t'.
5797 static svn_error_t *
5798 set_props_txn(svn_wc__db_wcroot_t *wcroot,
5799 const char *local_relpath,
5801 svn_boolean_t clear_recorded_info,
5802 const svn_skel_t *conflict,
5803 const svn_skel_t *work_items,
5804 apr_pool_t *scratch_pool)
5806 apr_hash_t *pristine_props;
5808 /* Check if the props are modified. If no changes, then wipe out the
5809 ACTUAL props. PRISTINE_PROPS==NULL means that any
5810 ACTUAL props are okay as provided, so go ahead and set them. */
5811 SVN_ERR(db_read_pristine_props(&pristine_props, wcroot, local_relpath, FALSE,
5812 scratch_pool, scratch_pool));
5813 if (props && pristine_props)
5815 apr_array_header_t *prop_diffs;
5817 SVN_ERR(svn_prop_diffs(&prop_diffs, props, pristine_props,
5819 if (prop_diffs->nelts == 0)
5823 SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath,
5824 props, wcroot->sdb, scratch_pool));
5826 if (clear_recorded_info)
5828 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
5829 SVN_INVALID_FILESIZE, 0,
5834 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5836 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
5837 conflict, scratch_pool));
5839 return SVN_NO_ERROR;
5844 svn_wc__db_op_set_props(svn_wc__db_t *db,
5845 const char *local_abspath,
5847 svn_boolean_t clear_recorded_info,
5848 const svn_skel_t *conflict,
5849 const svn_skel_t *work_items,
5850 apr_pool_t *scratch_pool)
5852 svn_wc__db_wcroot_t *wcroot;
5853 const char *local_relpath;
5855 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5857 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
5858 db, local_abspath, scratch_pool, scratch_pool));
5859 VERIFY_USABLE_WCROOT(wcroot);
5861 SVN_WC__DB_WITH_TXN(set_props_txn(wcroot, local_relpath, props,
5862 clear_recorded_info, conflict, work_items,
5865 return SVN_NO_ERROR;
5870 svn_wc__db_op_modified(svn_wc__db_t *db,
5871 const char *local_abspath,
5872 apr_pool_t *scratch_pool)
5874 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5880 static svn_error_t *
5881 populate_targets_tree(svn_wc__db_wcroot_t *wcroot,
5882 const char *local_relpath,
5884 const apr_array_header_t *changelist_filter,
5885 apr_pool_t *scratch_pool)
5887 svn_sqlite__stmt_t *stmt;
5888 int affected_rows = 0;
5889 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
5890 STMT_CREATE_TARGETS_LIST));
5892 if (changelist_filter && changelist_filter->nelts > 0)
5894 /* Iterate over the changelists, adding the nodes which match.
5895 Common case: we only have one changelist, so this only
5902 case svn_depth_empty:
5903 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST;
5906 case svn_depth_files:
5907 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES;
5910 case svn_depth_immediates:
5911 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES;
5914 case svn_depth_infinity:
5915 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY;
5919 /* We don't know how to handle unknown or exclude. */
5920 SVN_ERR_MALFUNCTION();
5924 for (i = 0; i < changelist_filter->nelts; i++)
5927 const char *changelist = APR_ARRAY_IDX(changelist_filter, i,
5930 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5931 STMT_INSERT_TARGET_WITH_CHANGELIST));
5932 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
5933 local_relpath, changelist));
5934 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5936 /* If the root is matched by the changelist, we don't have to match
5937 the children. As that tells us the root is a file */
5938 if (!sub_affected && depth > svn_depth_empty)
5940 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
5941 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
5942 local_relpath, changelist));
5943 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5946 affected_rows += sub_affected;
5949 else /* No changelist filtering */
5956 case svn_depth_empty:
5957 stmt_idx = STMT_INSERT_TARGET;
5960 case svn_depth_files:
5961 stmt_idx = STMT_INSERT_TARGET_DEPTH_FILES;
5964 case svn_depth_immediates:
5965 stmt_idx = STMT_INSERT_TARGET_DEPTH_IMMEDIATES;
5968 case svn_depth_infinity:
5969 stmt_idx = STMT_INSERT_TARGET_DEPTH_INFINITY;
5973 /* We don't know how to handle unknown or exclude. */
5974 SVN_ERR_MALFUNCTION();
5978 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5979 STMT_INSERT_TARGET));
5980 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
5981 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5982 affected_rows += sub_affected;
5984 if (depth > svn_depth_empty)
5986 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
5987 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
5988 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5989 affected_rows += sub_affected;
5993 /* Does the target exist? */
5994 if (affected_rows == 0)
5996 svn_boolean_t exists;
5997 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
6000 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6001 _("The node '%s' was not found."),
6002 path_for_error_message(wcroot,
6007 return SVN_NO_ERROR;
6012 static svn_error_t *
6013 dump_targets(svn_wc__db_wcroot_t *wcroot,
6014 apr_pool_t *scratch_pool)
6016 svn_sqlite__stmt_t *stmt;
6017 svn_boolean_t have_row;
6019 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6020 STMT_SELECT_TARGETS));
6021 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6024 const char *target = svn_sqlite__column_text(stmt, 0, NULL);
6025 SVN_DBG(("Target: '%s'\n", target));
6026 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6029 SVN_ERR(svn_sqlite__reset(stmt));
6031 return SVN_NO_ERROR;
6036 struct set_changelist_baton_t
6038 const char *new_changelist;
6039 const apr_array_header_t *changelist_filter;
6044 /* The main part of svn_wc__db_op_set_changelist().
6046 * Implements svn_wc__db_txn_callback_t. */
6047 static svn_error_t *
6048 set_changelist_txn(void *baton,
6049 svn_wc__db_wcroot_t *wcroot,
6050 const char *local_relpath,
6051 apr_pool_t *scratch_pool)
6053 struct set_changelist_baton_t *scb = baton;
6054 svn_sqlite__stmt_t *stmt;
6056 SVN_ERR(populate_targets_tree(wcroot, local_relpath, scb->depth,
6057 scb->changelist_filter, scratch_pool));
6059 /* Ensure we have actual nodes for our targets. */
6060 if (scb->new_changelist)
6062 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6063 STMT_INSERT_ACTUAL_EMPTIES));
6064 SVN_ERR(svn_sqlite__step_done(stmt));
6067 /* Now create our notification table. */
6068 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6069 STMT_CREATE_CHANGELIST_LIST));
6070 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6071 STMT_CREATE_CHANGELIST_TRIGGER));
6073 /* Update our changelists. */
6074 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6075 STMT_UPDATE_ACTUAL_CHANGELISTS));
6076 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6077 scb->new_changelist));
6078 SVN_ERR(svn_sqlite__step_done(stmt));
6080 if (scb->new_changelist)
6082 /* We have to notify that we skipped directories, so do that now. */
6083 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6084 STMT_MARK_SKIPPED_CHANGELIST_DIRS));
6085 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6086 scb->new_changelist));
6087 SVN_ERR(svn_sqlite__step_done(stmt));
6090 /* We may have left empty ACTUAL nodes, so remove them. This is only a
6091 potential problem if we removed changelists. */
6092 if (!scb->new_changelist)
6094 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6095 STMT_DELETE_ACTUAL_EMPTIES));
6096 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6097 SVN_ERR(svn_sqlite__step_done(stmt));
6100 return SVN_NO_ERROR;
6104 /* Send notifications for svn_wc__db_op_set_changelist().
6106 * Implements work_callback_t. */
6107 static svn_error_t *
6108 do_changelist_notify(void *baton,
6109 svn_wc__db_wcroot_t *wcroot,
6110 svn_cancel_func_t cancel_func,
6112 svn_wc_notify_func2_t notify_func,
6114 apr_pool_t *scratch_pool)
6116 svn_sqlite__stmt_t *stmt;
6117 svn_boolean_t have_row;
6118 apr_pool_t *iterpool;
6120 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6121 STMT_SELECT_CHANGELIST_LIST));
6122 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6124 iterpool = svn_pool_create(scratch_pool);
6127 /* ### wc_id is column 0. use it one day... */
6128 const char *notify_relpath = svn_sqlite__column_text(stmt, 1, NULL);
6129 svn_wc_notify_action_t action = svn_sqlite__column_int(stmt, 2);
6130 svn_wc_notify_t *notify;
6131 const char *notify_abspath;
6133 svn_pool_clear(iterpool);
6137 svn_error_t *err = cancel_func(cancel_baton);
6140 return svn_error_trace(svn_error_compose_create(
6142 svn_sqlite__reset(stmt)));
6145 notify_abspath = svn_dirent_join(wcroot->abspath, notify_relpath,
6147 notify = svn_wc_create_notify(notify_abspath, action, iterpool);
6148 notify->changelist_name = svn_sqlite__column_text(stmt, 3, NULL);
6149 notify_func(notify_baton, notify, iterpool);
6151 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6153 svn_pool_destroy(iterpool);
6155 return svn_error_trace(svn_sqlite__reset(stmt));
6160 svn_wc__db_op_set_changelist(svn_wc__db_t *db,
6161 const char *local_abspath,
6162 const char *new_changelist,
6163 const apr_array_header_t *changelist_filter,
6165 svn_wc_notify_func2_t notify_func,
6167 svn_cancel_func_t cancel_func,
6169 apr_pool_t *scratch_pool)
6171 svn_wc__db_wcroot_t *wcroot;
6172 const char *local_relpath;
6173 struct set_changelist_baton_t scb;
6175 scb.new_changelist = new_changelist;
6176 scb.changelist_filter = changelist_filter;
6179 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6181 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6183 scratch_pool, scratch_pool));
6184 VERIFY_USABLE_WCROOT(wcroot);
6186 /* Flush the entries before we do the work. Even if no work is performed,
6187 the flush isn't a problem. */
6188 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6190 /* Perform the set-changelist operation (transactionally), perform any
6191 notifications necessary, and then clean out our temporary tables. */
6192 return svn_error_trace(with_finalization(wcroot, local_relpath,
6193 set_changelist_txn, &scb,
6194 do_changelist_notify, NULL,
6195 cancel_func, cancel_baton,
6196 notify_func, notify_baton,
6197 STMT_FINALIZE_CHANGELIST,
6201 /* Implementation of svn_wc__db_op_mark_conflict() */
6203 svn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t *wcroot,
6204 const char *local_relpath,
6205 const svn_skel_t *conflict_skel,
6206 apr_pool_t *scratch_pool)
6208 svn_sqlite__stmt_t *stmt;
6209 svn_boolean_t got_row;
6210 svn_boolean_t is_complete;
6212 SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict_skel));
6213 SVN_ERR_ASSERT(is_complete);
6215 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6216 STMT_SELECT_ACTUAL_NODE));
6217 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6218 SVN_ERR(svn_sqlite__step(&got_row, stmt));
6219 SVN_ERR(svn_sqlite__reset(stmt));
6223 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6224 STMT_UPDATE_ACTUAL_CONFLICT));
6225 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6229 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6230 STMT_INSERT_ACTUAL_CONFLICT));
6231 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6232 if (*local_relpath != '\0')
6233 SVN_ERR(svn_sqlite__bind_text(stmt, 4,
6234 svn_relpath_dirname(local_relpath,
6239 svn_stringbuf_t *sb = svn_skel__unparse(conflict_skel, scratch_pool);
6241 SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6244 SVN_ERR(svn_sqlite__update(NULL, stmt));
6246 return SVN_NO_ERROR;
6250 svn_wc__db_op_mark_conflict(svn_wc__db_t *db,
6251 const char *local_abspath,
6252 const svn_skel_t *conflict_skel,
6253 const svn_skel_t *work_items,
6254 apr_pool_t *scratch_pool)
6256 svn_wc__db_wcroot_t *wcroot;
6257 const char *local_relpath;
6259 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6261 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6262 local_abspath, scratch_pool, scratch_pool));
6263 VERIFY_USABLE_WCROOT(wcroot);
6265 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6266 conflict_skel, scratch_pool));
6268 /* ### Should be handled in the same transaction as setting the conflict */
6270 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6272 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6274 return SVN_NO_ERROR;
6278 /* The body of svn_wc__db_op_mark_resolved().
6280 static svn_error_t *
6281 db_op_mark_resolved(svn_wc__db_wcroot_t *wcroot,
6282 const char *local_relpath,
6284 svn_boolean_t resolved_text,
6285 svn_boolean_t resolved_props,
6286 svn_boolean_t resolved_tree,
6287 const svn_skel_t *work_items,
6288 apr_pool_t *scratch_pool)
6290 svn_sqlite__stmt_t *stmt;
6291 svn_boolean_t have_row;
6292 int total_affected_rows = 0;
6293 svn_boolean_t resolved_all;
6294 apr_size_t conflict_len;
6295 const void *conflict_data;
6296 svn_skel_t *conflicts;
6298 /* Check if we have a conflict in ACTUAL */
6299 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6300 STMT_SELECT_ACTUAL_NODE));
6301 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6303 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6307 SVN_ERR(svn_sqlite__reset(stmt));
6309 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6310 STMT_SELECT_NODE_INFO));
6312 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6314 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6315 SVN_ERR(svn_sqlite__reset(stmt));
6318 return SVN_NO_ERROR;
6320 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6321 _("The node '%s' was not found."),
6322 path_for_error_message(wcroot,
6327 conflict_data = svn_sqlite__column_blob(stmt, 2, &conflict_len,
6329 conflicts = svn_skel__parse(conflict_data, conflict_len, scratch_pool);
6330 SVN_ERR(svn_sqlite__reset(stmt));
6332 SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
6333 db, wcroot->abspath,
6335 resolved_props ? "" : NULL,
6337 scratch_pool, scratch_pool));
6339 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6340 STMT_UPDATE_ACTUAL_CONFLICT));
6341 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6345 svn_stringbuf_t *sb = svn_skel__unparse(conflicts, scratch_pool);
6347 SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6350 SVN_ERR(svn_sqlite__update(&total_affected_rows, stmt));
6352 /* Now, remove the actual node if it doesn't have any more useful
6353 information. We only need to do this if we've remove data ourselves. */
6354 if (total_affected_rows > 0)
6356 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6357 STMT_DELETE_ACTUAL_EMPTY));
6358 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6359 SVN_ERR(svn_sqlite__step_done(stmt));
6362 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6364 return SVN_NO_ERROR;
6368 svn_wc__db_op_mark_resolved(svn_wc__db_t *db,
6369 const char *local_abspath,
6370 svn_boolean_t resolved_text,
6371 svn_boolean_t resolved_props,
6372 svn_boolean_t resolved_tree,
6373 const svn_skel_t *work_items,
6374 apr_pool_t *scratch_pool)
6376 svn_wc__db_wcroot_t *wcroot;
6377 const char *local_relpath;
6379 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6381 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6382 local_abspath, scratch_pool, scratch_pool));
6383 VERIFY_USABLE_WCROOT(wcroot);
6385 SVN_WC__DB_WITH_TXN(
6386 db_op_mark_resolved(wcroot, local_relpath, db,
6387 resolved_text, resolved_props, resolved_tree,
6388 work_items, scratch_pool),
6391 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6392 return SVN_NO_ERROR;
6395 /* Clear moved-to information at the delete-half of the move which
6396 * moved LOCAL_RELPATH here. This transforms the move into a simple delete. */
6397 static svn_error_t *
6398 clear_moved_to(const char *local_relpath,
6399 svn_wc__db_wcroot_t *wcroot,
6400 apr_pool_t *scratch_pool)
6402 svn_sqlite__stmt_t *stmt;
6403 svn_boolean_t have_row;
6404 const char *moved_from_relpath;
6406 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6407 STMT_SELECT_MOVED_FROM_RELPATH));
6408 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6409 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6412 SVN_ERR(svn_sqlite__reset(stmt));
6413 return SVN_NO_ERROR;
6416 moved_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
6417 SVN_ERR(svn_sqlite__reset(stmt));
6419 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6420 STMT_CLEAR_MOVED_TO_RELPATH));
6421 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6423 relpath_depth(moved_from_relpath)));
6424 SVN_ERR(svn_sqlite__step_done(stmt));
6426 return SVN_NO_ERROR;
6429 /* One of the two alternative bodies of svn_wc__db_op_revert().
6431 * Implements svn_wc__db_txn_callback_t. */
6432 static svn_error_t *
6433 op_revert_txn(void *baton,
6434 svn_wc__db_wcroot_t *wcroot,
6435 const char *local_relpath,
6436 apr_pool_t *scratch_pool)
6438 svn_wc__db_t *db = baton;
6439 svn_sqlite__stmt_t *stmt;
6440 svn_boolean_t have_row;
6442 svn_boolean_t moved_here;
6444 const char *moved_to;
6446 /* ### Similar structure to op_revert_recursive_txn, should they be
6449 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6450 STMT_SELECT_NODE_INFO));
6451 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6452 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6455 SVN_ERR(svn_sqlite__reset(stmt));
6457 /* There was no NODE row, so attempt to delete an ACTUAL_NODE row. */
6458 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6459 STMT_DELETE_ACTUAL_NODE));
6460 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6461 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6464 /* Can't do non-recursive actual-only revert if actual-only
6465 children exist. Raise an error to cancel the transaction. */
6466 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6467 STMT_ACTUAL_HAS_CHILDREN));
6468 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6469 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6470 SVN_ERR(svn_sqlite__reset(stmt));
6472 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6473 _("Can't revert '%s' without"
6474 " reverting children"),
6475 path_for_error_message(wcroot,
6478 return SVN_NO_ERROR;
6481 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6482 _("The node '%s' was not found."),
6483 path_for_error_message(wcroot,
6488 op_depth = svn_sqlite__column_int(stmt, 0);
6489 moved_here = svn_sqlite__column_boolean(stmt, 15);
6490 moved_to = svn_sqlite__column_text(stmt, 17, scratch_pool);
6491 SVN_ERR(svn_sqlite__reset(stmt));
6495 SVN_ERR(svn_wc__db_resolve_break_moved_away_internal(wcroot,
6502 svn_skel_t *conflict;
6504 SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, wcroot,
6506 scratch_pool, scratch_pool));
6509 svn_wc_operation_t operation;
6510 svn_boolean_t tree_conflicted;
6512 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
6514 db, wcroot->abspath,
6516 scratch_pool, scratch_pool));
6518 && (operation == svn_wc_operation_update
6519 || operation == svn_wc_operation_switch))
6521 svn_wc_conflict_reason_t reason;
6522 svn_wc_conflict_action_t action;
6524 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
6526 db, wcroot->abspath,
6531 if (reason == svn_wc_conflict_reason_deleted)
6532 SVN_ERR(svn_wc__db_resolve_delete_raise_moved_away(
6533 db, svn_dirent_join(wcroot->abspath, local_relpath,
6535 NULL, NULL /* ### How do we notify this? */,
6541 if (op_depth > 0 && op_depth == relpath_depth(local_relpath))
6543 /* Can't do non-recursive revert if children exist */
6544 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6545 STMT_SELECT_GE_OP_DEPTH_CHILDREN));
6546 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6547 local_relpath, op_depth));
6548 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6549 SVN_ERR(svn_sqlite__reset(stmt));
6551 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6552 _("Can't revert '%s' without"
6553 " reverting children"),
6554 path_for_error_message(wcroot,
6558 /* Rewrite the op-depth of all deleted children making the
6559 direct children into roots of deletes. */
6560 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6561 STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE));
6562 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6565 SVN_ERR(svn_sqlite__step_done(stmt));
6567 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6568 STMT_DELETE_WORKING_NODE));
6569 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6570 SVN_ERR(svn_sqlite__step_done(stmt));
6572 /* ### This removes the lock, but what about the access baton? */
6573 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6574 STMT_DELETE_WC_LOCK_ORPHAN));
6575 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6576 SVN_ERR(svn_sqlite__step_done(stmt));
6578 /* If this node was moved-here, clear moved-to at the move source. */
6580 SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool));
6583 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6584 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST));
6585 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6586 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6589 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6590 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST));
6591 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6592 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6595 return SVN_NO_ERROR;
6599 /* One of the two alternative bodies of svn_wc__db_op_revert().
6601 * Implements svn_wc__db_txn_callback_t. */
6602 static svn_error_t *
6603 op_revert_recursive_txn(void *baton,
6604 svn_wc__db_wcroot_t *wcroot,
6605 const char *local_relpath,
6606 apr_pool_t *scratch_pool)
6608 svn_sqlite__stmt_t *stmt;
6609 svn_boolean_t have_row;
6611 int select_op_depth;
6612 svn_boolean_t moved_here;
6614 apr_pool_t *iterpool;
6616 /* ### Similar structure to op_revert_txn, should they be
6619 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6620 STMT_SELECT_NODE_INFO));
6621 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6622 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6625 SVN_ERR(svn_sqlite__reset(stmt));
6627 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6628 STMT_DELETE_ACTUAL_NODE));
6629 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
6631 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6634 return SVN_NO_ERROR; /* actual-only revert */
6636 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6637 _("The node '%s' was not found."),
6638 path_for_error_message(wcroot,
6643 op_depth = svn_sqlite__column_int(stmt, 0);
6644 moved_here = svn_sqlite__column_boolean(stmt, 15);
6645 SVN_ERR(svn_sqlite__reset(stmt));
6647 if (op_depth > 0 && op_depth != relpath_depth(local_relpath))
6648 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6649 _("Can't revert '%s' without"
6650 " reverting parent"),
6651 path_for_error_message(wcroot,
6655 /* Remove moved-here from move destinations outside the tree. */
6656 SVN_ERR(svn_sqlite__get_statement(
6657 &stmt, wcroot->sdb, STMT_SELECT_MOVED_OUTSIDE));
6658 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
6660 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6663 const char *move_src_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6664 int move_op_depth = svn_sqlite__column_int(stmt, 2);
6667 err = svn_wc__db_resolve_break_moved_away_internal(wcroot,
6672 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
6674 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6676 SVN_ERR(svn_sqlite__reset(stmt));
6678 /* Don't delete BASE nodes */
6679 select_op_depth = op_depth ? op_depth : 1;
6681 /* Reverting any non wc-root node */
6682 SVN_ERR(svn_sqlite__get_statement(
6684 STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
6685 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6686 local_relpath, select_op_depth));
6687 SVN_ERR(svn_sqlite__step_done(stmt));
6689 SVN_ERR(svn_sqlite__get_statement(
6691 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
6692 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6693 SVN_ERR(svn_sqlite__step_done(stmt));
6695 SVN_ERR(svn_sqlite__get_statement(
6697 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
6698 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6699 SVN_ERR(svn_sqlite__step_done(stmt));
6701 /* ### This removes the locks, but what about the access batons? */
6702 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6703 STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
6704 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
6706 SVN_ERR(svn_sqlite__step_done(stmt));
6708 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6709 STMT_SELECT_MOVED_HERE_CHILDREN));
6710 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6712 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6714 iterpool = svn_pool_create(scratch_pool);
6717 const char *moved_here_child_relpath;
6720 svn_pool_clear(iterpool);
6722 moved_here_child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
6723 err = clear_moved_to(moved_here_child_relpath, wcroot, iterpool);
6725 return svn_error_trace(svn_error_compose_create(
6727 svn_sqlite__reset(stmt)));
6729 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6731 SVN_ERR(svn_sqlite__reset(stmt));
6732 svn_pool_destroy(iterpool);
6734 /* Clear potential moved-to pointing at the target node itself. */
6735 if (op_depth > 0 && op_depth == relpath_depth(local_relpath)
6737 SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool));
6739 return SVN_NO_ERROR;
6743 svn_wc__db_op_revert(svn_wc__db_t *db,
6744 const char *local_abspath,
6746 apr_pool_t *result_pool,
6747 apr_pool_t *scratch_pool)
6749 svn_wc__db_wcroot_t *wcroot;
6750 const char *local_relpath;
6751 struct with_triggers_baton_t wtb = { STMT_CREATE_REVERT_LIST,
6752 STMT_DROP_REVERT_LIST_TRIGGERS,
6755 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6759 case svn_depth_empty:
6760 wtb.cb_func = op_revert_txn;
6763 case svn_depth_infinity:
6764 wtb.cb_func = op_revert_recursive_txn;
6767 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
6768 _("Unsupported depth for revert of '%s'"),
6769 svn_dirent_local_style(local_abspath,
6773 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6774 db, local_abspath, scratch_pool, scratch_pool));
6775 VERIFY_USABLE_WCROOT(wcroot);
6777 SVN_WC__DB_WITH_TXN(with_triggers(&wtb, wcroot, local_relpath, scratch_pool),
6780 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6782 return SVN_NO_ERROR;
6785 /* The body of svn_wc__db_revert_list_read().
6787 static svn_error_t *
6788 revert_list_read(svn_boolean_t *reverted,
6789 const apr_array_header_t **marker_paths,
6790 svn_boolean_t *copied_here,
6791 svn_node_kind_t *kind,
6792 svn_wc__db_wcroot_t *wcroot,
6793 const char *local_relpath,
6795 apr_pool_t *result_pool,
6796 apr_pool_t *scratch_pool)
6798 svn_sqlite__stmt_t *stmt;
6799 svn_boolean_t have_row;
6802 *marker_paths = NULL;
6803 *copied_here = FALSE;
6804 *kind = svn_node_unknown;
6806 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6807 STMT_SELECT_REVERT_LIST));
6808 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6809 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6812 svn_boolean_t is_actual = svn_sqlite__column_boolean(stmt, 0);
6813 svn_boolean_t another_row = FALSE;
6817 apr_size_t conflict_len;
6818 const void *conflict_data;
6820 conflict_data = svn_sqlite__column_blob(stmt, 5, &conflict_len,
6824 svn_skel_t *conflicts = svn_skel__parse(conflict_data,
6828 SVN_ERR(svn_wc__conflict_read_markers(marker_paths,
6829 db, wcroot->abspath,
6835 if (!svn_sqlite__column_is_null(stmt, 1)) /* notify */
6838 SVN_ERR(svn_sqlite__step(&another_row, stmt));
6841 if (!is_actual || another_row)
6844 if (!svn_sqlite__column_is_null(stmt, 4)) /* repos_id */
6846 int op_depth = svn_sqlite__column_int(stmt, 3);
6847 *copied_here = (op_depth == relpath_depth(local_relpath));
6849 *kind = svn_sqlite__column_token(stmt, 2, kind_map);
6853 SVN_ERR(svn_sqlite__reset(stmt));
6857 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6858 STMT_DELETE_REVERT_LIST));
6859 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6860 SVN_ERR(svn_sqlite__step_done(stmt));
6863 return SVN_NO_ERROR;
6867 svn_wc__db_revert_list_read(svn_boolean_t *reverted,
6868 const apr_array_header_t **marker_files,
6869 svn_boolean_t *copied_here,
6870 svn_node_kind_t *kind,
6872 const char *local_abspath,
6873 apr_pool_t *result_pool,
6874 apr_pool_t *scratch_pool)
6876 svn_wc__db_wcroot_t *wcroot;
6877 const char *local_relpath;
6879 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6880 db, local_abspath, scratch_pool, scratch_pool));
6881 VERIFY_USABLE_WCROOT(wcroot);
6883 SVN_WC__DB_WITH_TXN(
6884 revert_list_read(reverted, marker_files, copied_here, kind,
6885 wcroot, local_relpath, db,
6886 result_pool, scratch_pool),
6888 return SVN_NO_ERROR;
6892 /* The body of svn_wc__db_revert_list_read_copied_children().
6894 static svn_error_t *
6895 revert_list_read_copied_children(svn_wc__db_wcroot_t *wcroot,
6896 const char *local_relpath,
6897 const apr_array_header_t **children_p,
6898 apr_pool_t *result_pool,
6899 apr_pool_t *scratch_pool)
6901 svn_sqlite__stmt_t *stmt;
6902 svn_boolean_t have_row;
6903 apr_array_header_t *children;
6906 apr_array_make(result_pool, 0,
6907 sizeof(svn_wc__db_revert_list_copied_child_info_t *));
6909 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6910 STMT_SELECT_REVERT_LIST_COPIED_CHILDREN));
6911 SVN_ERR(svn_sqlite__bindf(stmt, "sd",
6912 local_relpath, relpath_depth(local_relpath)));
6913 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6916 svn_wc__db_revert_list_copied_child_info_t *child_info;
6917 const char *child_relpath;
6919 child_info = apr_palloc(result_pool, sizeof(*child_info));
6921 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6922 child_info->abspath = svn_dirent_join(wcroot->abspath, child_relpath,
6924 child_info->kind = svn_sqlite__column_token(stmt, 1, kind_map);
6927 svn_wc__db_revert_list_copied_child_info_t *) = child_info;
6929 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6931 SVN_ERR(svn_sqlite__reset(stmt));
6933 *children_p = children;
6935 return SVN_NO_ERROR;
6940 svn_wc__db_revert_list_read_copied_children(const apr_array_header_t **children,
6942 const char *local_abspath,
6943 apr_pool_t *result_pool,
6944 apr_pool_t *scratch_pool)
6946 svn_wc__db_wcroot_t *wcroot;
6947 const char *local_relpath;
6949 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6950 db, local_abspath, scratch_pool, scratch_pool));
6951 VERIFY_USABLE_WCROOT(wcroot);
6953 SVN_WC__DB_WITH_TXN(
6954 revert_list_read_copied_children(wcroot, local_relpath, children,
6955 result_pool, scratch_pool),
6957 return SVN_NO_ERROR;
6962 svn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func,
6965 const char *local_abspath,
6966 apr_pool_t *scratch_pool)
6968 svn_wc__db_wcroot_t *wcroot;
6969 const char *local_relpath;
6970 svn_sqlite__stmt_t *stmt;
6971 svn_boolean_t have_row;
6972 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
6974 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6975 db, local_abspath, scratch_pool, iterpool));
6976 VERIFY_USABLE_WCROOT(wcroot);
6978 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6979 STMT_SELECT_REVERT_LIST_RECURSIVE));
6980 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6981 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6983 return svn_error_trace(svn_sqlite__reset(stmt)); /* optimise for no row */
6986 const char *notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6988 svn_pool_clear(iterpool);
6990 notify_func(notify_baton,
6991 svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
6994 svn_wc_notify_revert,
6998 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7000 SVN_ERR(svn_sqlite__reset(stmt));
7002 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7003 STMT_DELETE_REVERT_LIST_RECURSIVE));
7004 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7005 SVN_ERR(svn_sqlite__step_done(stmt));
7007 svn_pool_destroy(iterpool);
7009 return SVN_NO_ERROR;
7013 svn_wc__db_revert_list_done(svn_wc__db_t *db,
7014 const char *local_abspath,
7015 apr_pool_t *scratch_pool)
7017 svn_wc__db_wcroot_t *wcroot;
7018 const char *local_relpath;
7020 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7021 db, local_abspath, scratch_pool, scratch_pool));
7022 VERIFY_USABLE_WCROOT(wcroot);
7024 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_DROP_REVERT_LIST));
7026 return SVN_NO_ERROR;
7029 /* The body of svn_wc__db_op_remove_node().
7031 static svn_error_t *
7032 remove_node_txn(svn_boolean_t *left_changes,
7033 svn_wc__db_wcroot_t *wcroot,
7034 const char *local_relpath,
7036 svn_boolean_t destroy_wc,
7037 svn_boolean_t destroy_changes,
7038 svn_revnum_t not_present_rev,
7039 svn_wc__db_status_t not_present_status,
7040 svn_node_kind_t not_present_kind,
7041 const svn_skel_t *conflict,
7042 const svn_skel_t *work_items,
7043 svn_cancel_func_t cancel_func,
7045 apr_pool_t *scratch_pool)
7047 svn_sqlite__stmt_t *stmt;
7049 apr_int64_t repos_id;
7050 const char *repos_relpath;
7052 /* Note that unlike many similar functions it is a valid scenario for this
7053 function to be called on a wcroot! */
7055 /* db set when destroying wc */
7056 SVN_ERR_ASSERT(!destroy_wc || db != NULL);
7059 *left_changes = FALSE;
7061 /* Need info for not_present node? */
7062 if (SVN_IS_VALID_REVNUM(not_present_rev))
7063 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
7064 &repos_relpath, &repos_id,
7065 NULL, NULL, NULL, NULL, NULL,
7066 NULL, NULL, NULL, NULL, NULL,
7067 wcroot, local_relpath,
7068 scratch_pool, scratch_pool));
7071 && (!destroy_changes || *local_relpath == '\0'))
7073 svn_boolean_t have_row;
7074 apr_pool_t *iterpool;
7075 svn_error_t *err = NULL;
7077 /* Install WQ items for deleting the unmodified files and all dirs */
7078 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7079 STMT_SELECT_WORKING_PRESENT));
7080 SVN_ERR(svn_sqlite__bindf(stmt, "is",
7081 wcroot->wc_id, local_relpath));
7083 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7085 iterpool = svn_pool_create(scratch_pool);
7089 const char *child_relpath;
7090 const char *child_abspath;
7091 svn_node_kind_t child_kind;
7092 svn_boolean_t have_checksum;
7093 svn_filesize_t recorded_size;
7094 apr_int64_t recorded_time;
7095 const svn_io_dirent2_t *dirent;
7096 svn_boolean_t modified_p = TRUE;
7097 svn_skel_t *work_item = NULL;
7099 svn_pool_clear(iterpool);
7101 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7102 child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
7104 child_abspath = svn_dirent_join(wcroot->abspath, child_relpath,
7107 if (child_kind == svn_node_file)
7109 have_checksum = !svn_sqlite__column_is_null(stmt, 2);
7110 recorded_size = get_recorded_size(stmt, 3);
7111 recorded_time = svn_sqlite__column_int64(stmt, 4);
7115 err = cancel_func(cancel_baton);
7120 err = svn_io_stat_dirent2(&dirent, child_abspath, FALSE, TRUE,
7121 iterpool, iterpool);
7127 || dirent->kind != svn_node_file
7128 || child_kind != svn_node_file)
7130 /* Not interested in keeping changes */
7133 else if (child_kind == svn_node_file
7134 && dirent->kind == svn_node_file
7135 && dirent->filesize == recorded_size
7136 && dirent->mtime == recorded_time)
7138 modified_p = FALSE; /* File matches recorded state */
7140 else if (have_checksum)
7141 err = svn_wc__internal_file_modified_p(&modified_p,
7151 *left_changes = TRUE;
7153 else if (child_kind == svn_node_dir)
7155 err = svn_wc__wq_build_dir_remove(&work_item,
7156 db, wcroot->abspath,
7157 child_abspath, FALSE,
7158 iterpool, iterpool);
7160 else /* svn_node_file || svn_node_symlink */
7162 err = svn_wc__wq_build_file_remove(&work_item,
7163 db, wcroot->abspath,
7165 iterpool, iterpool);
7173 err = add_work_items(wcroot->sdb, work_item, iterpool);
7178 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7180 svn_pool_destroy(iterpool);
7182 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
7185 if (destroy_wc && *local_relpath != '\0')
7187 /* Create work item for destroying the root */
7188 svn_wc__db_status_t status;
7189 svn_node_kind_t kind;
7190 SVN_ERR(read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, NULL,
7191 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7192 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7193 wcroot, local_relpath,
7194 scratch_pool, scratch_pool));
7196 if (status == svn_wc__db_status_normal
7197 || status == svn_wc__db_status_added
7198 || status == svn_wc__db_status_incomplete)
7200 svn_skel_t *work_item = NULL;
7201 const char *local_abspath = svn_dirent_join(wcroot->abspath,
7205 if (kind == svn_node_dir)
7207 SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
7208 db, wcroot->abspath,
7212 scratch_pool, scratch_pool));
7216 svn_boolean_t modified_p = FALSE;
7218 if (!destroy_changes)
7220 SVN_ERR(svn_wc__internal_file_modified_p(&modified_p,
7227 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
7228 db, wcroot->abspath,
7235 *left_changes = TRUE;
7239 SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
7243 /* Remove all nodes below local_relpath */
7244 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7245 STMT_DELETE_NODE_RECURSIVE));
7246 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7247 SVN_ERR(svn_sqlite__step_done(stmt));
7249 /* Delete the root NODE when this is not the working copy root */
7250 if (local_relpath[0] != '\0')
7252 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7253 STMT_DELETE_NODE_ALL_LAYERS));
7254 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7255 SVN_ERR(svn_sqlite__step_done(stmt));
7258 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7259 STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7261 /* Delete all actual nodes at or below local_relpath */
7262 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7264 SVN_ERR(svn_sqlite__step_done(stmt));
7266 /* Should we leave a not-present node? */
7267 if (SVN_IS_VALID_REVNUM(not_present_rev))
7269 insert_base_baton_t ibb;
7272 ibb.repos_id = repos_id;
7274 SVN_ERR_ASSERT(not_present_status == svn_wc__db_status_not_present
7275 || not_present_status == svn_wc__db_status_excluded);
7277 ibb.status = not_present_status;
7278 ibb.kind = not_present_kind;
7280 ibb.repos_relpath = repos_relpath;
7281 ibb.revision = not_present_rev;
7283 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
7286 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
7288 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
7289 conflict, scratch_pool));
7291 return SVN_NO_ERROR;
7295 svn_wc__db_op_remove_node(svn_boolean_t *left_changes,
7297 const char *local_abspath,
7298 svn_boolean_t destroy_wc,
7299 svn_boolean_t destroy_changes,
7300 svn_revnum_t not_present_revision,
7301 svn_wc__db_status_t not_present_status,
7302 svn_node_kind_t not_present_kind,
7303 const svn_skel_t *conflict,
7304 const svn_skel_t *work_items,
7305 svn_cancel_func_t cancel_func,
7307 apr_pool_t *scratch_pool)
7309 svn_wc__db_wcroot_t *wcroot;
7310 const char *local_relpath;
7312 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7314 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7315 local_abspath, scratch_pool, scratch_pool));
7316 VERIFY_USABLE_WCROOT(wcroot);
7318 SVN_WC__DB_WITH_TXN(remove_node_txn(left_changes,
7319 wcroot, local_relpath, db,
7320 destroy_wc, destroy_changes,
7321 not_present_revision, not_present_status,
7322 not_present_kind, conflict, work_items,
7323 cancel_func, cancel_baton, scratch_pool),
7326 /* Flush everything below this node in all ways */
7327 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
7330 return SVN_NO_ERROR;
7334 /* The body of svn_wc__db_op_set_base_depth().
7336 static svn_error_t *
7337 db_op_set_base_depth(svn_wc__db_wcroot_t *wcroot,
7338 const char *local_relpath,
7340 apr_pool_t *scratch_pool)
7342 svn_sqlite__stmt_t *stmt;
7345 /* Flush any entries before we start monkeying the database. */
7346 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7347 STMT_UPDATE_NODE_BASE_DEPTH));
7348 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
7349 svn_token__to_word(depth_map, depth)));
7350 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7352 if (affected_rows == 0)
7353 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7354 "The node '%s' is not a committed directory",
7355 path_for_error_message(wcroot, local_relpath,
7358 return SVN_NO_ERROR;
7363 svn_wc__db_op_set_base_depth(svn_wc__db_t *db,
7364 const char *local_abspath,
7366 apr_pool_t *scratch_pool)
7368 svn_wc__db_wcroot_t *wcroot;
7369 const char *local_relpath;
7371 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7372 SVN_ERR_ASSERT(depth >= svn_depth_empty && depth <= svn_depth_infinity);
7374 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7375 local_abspath, scratch_pool, scratch_pool));
7376 VERIFY_USABLE_WCROOT(wcroot);
7378 /* ### We set depth on working and base to match entry behavior.
7379 Maybe these should be separated later? */
7380 SVN_WC__DB_WITH_TXN(db_op_set_base_depth(wcroot, local_relpath, depth,
7384 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
7386 return SVN_NO_ERROR;
7390 static svn_error_t *
7391 info_below_working(svn_boolean_t *have_base,
7392 svn_boolean_t *have_work,
7393 svn_wc__db_status_t *status,
7394 svn_wc__db_wcroot_t *wcroot,
7395 const char *local_relpath,
7396 int below_op_depth, /* < 0 is ignored */
7397 apr_pool_t *scratch_pool);
7400 /* Convert STATUS, the raw status obtained from the presence map, to
7401 the status appropriate for a working (op_depth > 0) node and return
7402 it in *WORKING_STATUS. */
7403 static svn_error_t *
7404 convert_to_working_status(svn_wc__db_status_t *working_status,
7405 svn_wc__db_status_t status)
7407 svn_wc__db_status_t work_status = status;
7409 SVN_ERR_ASSERT(work_status == svn_wc__db_status_normal
7410 || work_status == svn_wc__db_status_not_present
7411 || work_status == svn_wc__db_status_base_deleted
7412 || work_status == svn_wc__db_status_incomplete
7413 || work_status == svn_wc__db_status_excluded);
7415 if (work_status == svn_wc__db_status_excluded)
7417 *working_status = svn_wc__db_status_excluded;
7419 else if (work_status == svn_wc__db_status_not_present
7420 || work_status == svn_wc__db_status_base_deleted)
7422 /* The caller should scan upwards to detect whether this
7423 deletion has occurred because this node has been moved
7424 away, or it is a regular deletion. Also note that the
7425 deletion could be of the BASE tree, or a child of
7426 something that has been copied/moved here. */
7428 *working_status = svn_wc__db_status_deleted;
7430 else /* normal or incomplete */
7432 /* The caller should scan upwards to detect whether this
7433 addition has occurred because of a simple addition,
7434 a copy, or is the destination of a move. */
7435 *working_status = svn_wc__db_status_added;
7438 return SVN_NO_ERROR;
7442 /* Return the status of the node, if any, below the "working" node (or
7443 below BELOW_OP_DEPTH if >= 0).
7444 Set *HAVE_BASE or *HAVE_WORK to indicate if a base node or lower
7445 working node is present, and *STATUS to the status of the first
7446 layer below the selected node. */
7447 static svn_error_t *
7448 info_below_working(svn_boolean_t *have_base,
7449 svn_boolean_t *have_work,
7450 svn_wc__db_status_t *status,
7451 svn_wc__db_wcroot_t *wcroot,
7452 const char *local_relpath,
7454 apr_pool_t *scratch_pool)
7456 svn_sqlite__stmt_t *stmt;
7457 svn_boolean_t have_row;
7459 *have_base = *have_work = FALSE;
7460 *status = svn_wc__db_status_normal;
7462 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7463 STMT_SELECT_NODE_INFO));
7464 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7465 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7467 if (below_op_depth >= 0)
7470 (svn_sqlite__column_int(stmt, 0) > below_op_depth))
7472 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7477 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7479 *status = svn_sqlite__column_token(stmt, 3, presence_map);
7483 int op_depth = svn_sqlite__column_int(stmt, 0);
7490 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7493 SVN_ERR(svn_sqlite__reset(stmt));
7496 SVN_ERR(convert_to_working_status(status, *status));
7498 return SVN_NO_ERROR;
7501 /* Helper function for op_delete_txn */
7502 static svn_error_t *
7503 delete_update_movedto(svn_wc__db_wcroot_t *wcroot,
7504 const char *child_moved_from_relpath,
7506 const char *new_moved_to_relpath,
7507 apr_pool_t *scratch_pool)
7509 svn_sqlite__stmt_t *stmt;
7512 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7513 STMT_UPDATE_MOVED_TO_RELPATH));
7515 SVN_ERR(svn_sqlite__bindf(stmt, "isds",
7517 child_moved_from_relpath,
7519 new_moved_to_relpath));
7520 SVN_ERR(svn_sqlite__update(&affected, stmt));
7521 assert(affected == 1);
7523 return SVN_NO_ERROR;
7527 struct op_delete_baton_t {
7528 const char *moved_to_relpath; /* NULL if delete is not part of a move */
7529 svn_skel_t *conflict;
7530 svn_skel_t *work_items;
7531 svn_boolean_t delete_dir_externals;
7532 svn_boolean_t notify;
7535 /* This structure is used while rewriting move information for nodes.
7537 * The most simple case of rewriting move information happens when
7538 * a moved-away subtree is moved again: mv A B; mv B C
7539 * The second move requires rewriting moved-to info at or within A.
7541 * Another example is a move of a subtree which had nodes moved into it:
7543 * This requires rewriting such that A/F is marked has having moved to G/F.
7545 * Another case is where a node becomes a nested moved node.
7546 * A nested move happens when a subtree child is moved before or after
7547 * the subtree itself is moved. For example:
7548 * mv A/F A/G; mv A B
7549 * In this case, the move A/F -> A/G is rewritten to B/F -> B/G.
7550 * Note that the following sequence results in the same DB state:
7551 * mv A B; mv B/F B/G
7552 * We do not care about the order the moves were performed in.
7553 * For details, see http://wiki.apache.org/subversion/MultiLayerMoves
7555 struct moved_node_t {
7556 /* The source of the move. */
7557 const char *local_relpath;
7559 /* The move destination. */
7560 const char *moved_to_relpath;
7562 /* The op-depth of the deleted node at the source of the move. */
7565 /* When >= 1 the op_depth at which local_relpath was moved to its
7566 location. Used to find its original location outside the delete */
7567 int moved_from_depth;
7570 /* Helper function to resolve the original location of local_relpath at OP_DEPTH
7571 before it was moved into the tree rooted at ROOT_RELPATH. */
7572 static svn_error_t *
7573 resolve_moved_from(const char **moved_from_relpath,
7574 int *moved_from_op_depth,
7575 svn_wc__db_wcroot_t *wcroot,
7576 const char *root_relpath,
7577 const char *local_relpath,
7579 apr_pool_t *result_pool,
7580 apr_pool_t *scratch_pool)
7582 const char *suffix = "";
7583 svn_sqlite__stmt_t *stmt;
7584 const char *m_from_relpath;
7585 int m_from_op_depth;
7586 int m_move_from_depth;
7587 svn_boolean_t have_row;
7589 while (relpath_depth(local_relpath) > op_depth)
7592 svn_relpath_split(&local_relpath, &name, local_relpath, scratch_pool);
7593 suffix = svn_relpath_join(suffix, name, scratch_pool);
7596 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7597 STMT_SELECT_MOVED_FROM_FOR_DELETE));
7598 SVN_ERR(svn_sqlite__bindf(stmt, "is",
7599 wcroot->wc_id, local_relpath));
7600 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7604 /* assert(have_row); */
7605 *moved_from_relpath = NULL;
7606 *moved_from_op_depth = -1;
7608 SVN_ERR(svn_sqlite__reset(stmt));
7610 return SVN_NO_ERROR;
7613 m_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
7614 m_from_op_depth = svn_sqlite__column_int(stmt, 1);
7615 m_move_from_depth = svn_sqlite__column_int(stmt, 2);
7617 SVN_ERR(svn_sqlite__reset(stmt));
7619 if (! svn_relpath_skip_ancestor(root_relpath, m_from_relpath))
7621 *moved_from_relpath = svn_relpath_join(m_from_relpath, suffix,
7623 *moved_from_op_depth = m_from_op_depth; /* ### Ok? */
7624 return SVN_NO_ERROR;
7626 else if (!m_move_from_depth)
7628 *moved_from_relpath = NULL;
7629 *moved_from_op_depth = -1;
7630 return SVN_NO_ERROR;
7633 return svn_error_trace(
7634 resolve_moved_from(moved_from_relpath,
7635 moved_from_op_depth,
7638 svn_relpath_join(m_from_relpath, suffix,
7641 result_pool, scratch_pool));
7644 static svn_error_t *
7645 delete_node(void *baton,
7646 svn_wc__db_wcroot_t *wcroot,
7647 const char *local_relpath,
7648 apr_pool_t *scratch_pool)
7650 struct op_delete_baton_t *b = baton;
7651 svn_wc__db_status_t status;
7652 svn_boolean_t have_row, op_root;
7653 svn_boolean_t add_work = FALSE;
7654 svn_sqlite__stmt_t *stmt;
7655 int working_op_depth; /* Depth of what is to be deleted */
7656 int keep_op_depth = 0; /* Depth of what is below what is deleted */
7657 svn_node_kind_t kind;
7658 apr_array_header_t *moved_nodes = NULL;
7659 int delete_op_depth = relpath_depth(local_relpath);
7661 assert(*local_relpath); /* Can't delete wcroot */
7663 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7664 STMT_SELECT_NODE_INFO));
7665 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7666 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7670 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
7671 svn_sqlite__reset(stmt),
7672 _("The node '%s' was not found."),
7673 path_for_error_message(wcroot,
7678 working_op_depth = svn_sqlite__column_int(stmt, 0);
7679 status = svn_sqlite__column_token(stmt, 3, presence_map);
7680 kind = svn_sqlite__column_token(stmt, 4, kind_map);
7682 if (working_op_depth < delete_op_depth)
7686 keep_op_depth = working_op_depth;
7692 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7696 svn_wc__db_status_t below_status;
7699 below_op_depth = svn_sqlite__column_int(stmt, 0);
7700 below_status = svn_sqlite__column_token(stmt, 3, presence_map);
7702 if (below_status != svn_wc__db_status_not_present
7703 && below_status != svn_wc__db_status_base_deleted)
7706 keep_op_depth = below_op_depth;
7715 SVN_ERR(svn_sqlite__reset(stmt));
7717 if (working_op_depth != 0) /* WORKING */
7718 SVN_ERR(convert_to_working_status(&status, status));
7720 if (status == svn_wc__db_status_deleted
7721 || status == svn_wc__db_status_not_present)
7722 return SVN_NO_ERROR;
7724 /* Don't copy BASE directories with server excluded nodes */
7725 if (status == svn_wc__db_status_normal && kind == svn_node_dir)
7727 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7728 STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
7729 SVN_ERR(svn_sqlite__bindf(stmt, "is",
7730 wcroot->wc_id, local_relpath));
7731 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7734 const char *absent_path = svn_sqlite__column_text(stmt, 0,
7737 return svn_error_createf(
7738 SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
7739 svn_sqlite__reset(stmt),
7740 _("Cannot delete '%s' as '%s' is excluded by server"),
7741 path_for_error_message(wcroot, local_relpath,
7743 path_for_error_message(wcroot, absent_path,
7746 SVN_ERR(svn_sqlite__reset(stmt));
7748 else if (status == svn_wc__db_status_server_excluded)
7750 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
7751 _("Cannot delete '%s' as it is excluded by server"),
7752 path_for_error_message(wcroot, local_relpath,
7755 else if (status == svn_wc__db_status_excluded)
7757 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
7758 _("Cannot delete '%s' as it is excluded"),
7759 path_for_error_message(wcroot, local_relpath,
7763 if (b->moved_to_relpath)
7765 const char *moved_from_relpath = NULL;
7766 struct moved_node_t *moved_node;
7769 moved_nodes = apr_array_make(scratch_pool, 1,
7770 sizeof(struct moved_node_t *));
7772 /* The node is being moved-away.
7773 * Figure out if the node was moved-here before, or whether this
7774 * is the first time the node is moved. */
7775 if (status == svn_wc__db_status_added)
7776 SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL,
7777 &moved_from_relpath,
7780 wcroot, local_relpath,
7781 scratch_pool, scratch_pool));
7783 if (op_root && moved_from_relpath)
7785 const char *part = svn_relpath_skip_ancestor(local_relpath,
7786 moved_from_relpath);
7788 /* Existing move-root is moved to another location */
7789 moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
7791 moved_node->local_relpath = moved_from_relpath;
7793 moved_node->local_relpath = svn_relpath_join(b->moved_to_relpath,
7794 part, scratch_pool);
7795 moved_node->op_depth = move_op_depth;
7796 moved_node->moved_to_relpath = b->moved_to_relpath;
7797 moved_node->moved_from_depth = -1;
7799 APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
7801 else if (!op_root && (status == svn_wc__db_status_normal
7802 || status == svn_wc__db_status_copied
7803 || status == svn_wc__db_status_moved_here))
7805 /* The node is becoming a move-root for the first time,
7806 * possibly because of a nested move operation. */
7807 moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
7808 moved_node->local_relpath = local_relpath;
7809 moved_node->op_depth = delete_op_depth;
7810 moved_node->moved_to_relpath = b->moved_to_relpath;
7811 moved_node->moved_from_depth = -1;
7813 APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
7815 /* Else: We can't track history of local additions and/or of things we are
7818 /* And update all moved_to values still pointing to this location */
7819 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7820 STMT_UPDATE_MOVED_TO_DESCENDANTS));
7821 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
7823 b->moved_to_relpath));
7824 SVN_ERR(svn_sqlite__update(NULL, stmt));
7827 /* Find children that were moved out of the subtree rooted at this node.
7828 * We'll need to update their op-depth columns because their deletion
7829 * is now implied by the deletion of their parent (i.e. this node). */
7831 apr_pool_t *iterpool;
7834 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7835 STMT_SELECT_MOVED_FOR_DELETE));
7836 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
7839 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7840 iterpool = svn_pool_create(scratch_pool);
7843 struct moved_node_t *mn;
7844 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7845 const char *mv_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
7846 int child_op_depth = svn_sqlite__column_int(stmt, 2);
7847 int moved_from_depth = -1;
7848 svn_boolean_t fixup = FALSE;
7850 if (! b->moved_to_relpath
7851 && ! svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
7853 /* a NULL moved_here_depth will be reported as 0 */
7854 int moved_here_depth = svn_sqlite__column_int(stmt, 3);
7856 /* Plain delete. Fixup move information of descendants that were
7857 moved here, or that were moved out */
7859 if (moved_here_depth >= delete_op_depth)
7861 /* The move we recorded here must be moved to the location
7862 this node had before it was moved here.
7864 This might contain multiple steps when the node was moved
7865 in several places within the to be deleted tree */
7867 /* ### TODO: Add logic */
7869 moved_from_depth = moved_here_depth;
7873 /* Update the op-depth of an moved away node that was
7874 registered as moved by the records that we are about
7877 child_op_depth = delete_op_depth;
7880 else if (b->moved_to_relpath)
7882 /* The node is moved to a new location */
7884 if (delete_op_depth == child_op_depth)
7886 /* Update the op-depth of a tree shadowed by this tree */
7888 /*child_op_depth = delete_depth;*/
7890 else if (child_op_depth >= delete_op_depth
7891 && !svn_relpath_skip_ancestor(local_relpath,
7894 /* Update the move destination of something that is now moved
7897 child_relpath = svn_relpath_skip_ancestor(local_relpath,
7902 child_relpath = svn_relpath_join(b->moved_to_relpath,
7906 if (child_op_depth > delete_op_depth
7907 && svn_relpath_skip_ancestor(local_relpath,
7909 child_op_depth = delete_op_depth;
7911 child_op_depth = relpath_depth(child_relpath);
7920 mn = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
7922 mn->local_relpath = apr_pstrdup(scratch_pool, child_relpath);
7923 mn->moved_to_relpath = apr_pstrdup(scratch_pool, mv_to_relpath);
7924 mn->op_depth = child_op_depth;
7925 mn->moved_from_depth = moved_from_depth;
7928 moved_nodes = apr_array_make(scratch_pool, 1,
7929 sizeof(struct moved_node_t *));
7930 APR_ARRAY_PUSH(moved_nodes, struct moved_node_t *) = mn;
7933 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7935 SVN_ERR(svn_sqlite__reset(stmt));
7937 for (i = 0; moved_nodes && (i < moved_nodes->nelts); i++)
7939 struct moved_node_t *mn = APR_ARRAY_IDX(moved_nodes, i,
7940 struct moved_node_t *);
7942 if (mn->moved_from_depth > 0)
7944 svn_pool_clear(iterpool);
7946 SVN_ERR(resolve_moved_from(&mn->local_relpath, &mn->op_depth,
7947 wcroot, local_relpath,
7949 mn->moved_from_depth,
7950 scratch_pool, iterpool));
7952 if (!mn->local_relpath)
7953 svn_sort__array_delete(moved_nodes, i--, 1);
7957 svn_pool_destroy(iterpool);
7960 if (!b->moved_to_relpath)
7962 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7963 STMT_CLEAR_MOVED_TO_DESCENDANTS));
7964 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7966 SVN_ERR(svn_sqlite__update(NULL, stmt));
7970 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7971 STMT_CLEAR_MOVED_TO_FROM_DEST));
7972 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7975 SVN_ERR(svn_sqlite__update(NULL, stmt));
7980 /* ### Put actual-only nodes into the list? */
7983 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7984 STMT_INSERT_DELETE_LIST));
7985 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
7986 wcroot->wc_id, local_relpath, working_op_depth));
7987 SVN_ERR(svn_sqlite__step_done(stmt));
7990 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7991 STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
7992 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
7993 wcroot->wc_id, local_relpath, delete_op_depth));
7994 SVN_ERR(svn_sqlite__step_done(stmt));
7996 /* Delete ACTUAL_NODE rows, but leave those that have changelist
7998 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7999 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8000 SVN_ERR(svn_sqlite__bindf(stmt, "is",
8001 wcroot->wc_id, local_relpath));
8002 SVN_ERR(svn_sqlite__step_done(stmt));
8004 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8005 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8006 SVN_ERR(svn_sqlite__bindf(stmt, "is",
8007 wcroot->wc_id, local_relpath));
8008 SVN_ERR(svn_sqlite__step_done(stmt));
8010 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8011 STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
8012 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8014 SVN_ERR(svn_sqlite__step_done(stmt));
8018 /* Delete the node at LOCAL_RELPATH, and possibly mark it as moved. */
8020 /* Delete the node and possible descendants. */
8021 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8022 STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
8023 SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
8024 wcroot->wc_id, local_relpath,
8025 keep_op_depth, delete_op_depth));
8026 SVN_ERR(svn_sqlite__step_done(stmt));
8033 for (i = 0; i < moved_nodes->nelts; ++i)
8035 const struct moved_node_t *moved_node
8036 = APR_ARRAY_IDX(moved_nodes, i, void *);
8038 SVN_ERR(delete_update_movedto(wcroot,
8039 moved_node->local_relpath,
8040 moved_node->op_depth,
8041 moved_node->moved_to_relpath,
8046 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8047 STMT_DELETE_FILE_EXTERNALS));
8048 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8049 SVN_ERR(svn_sqlite__step_done(stmt));
8051 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8052 b->delete_dir_externals
8053 ? STMT_DELETE_EXTERNAL_REGISTATIONS
8054 : STMT_DELETE_FILE_EXTERNAL_REGISTATIONS));
8055 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8056 SVN_ERR(svn_sqlite__step_done(stmt));
8058 SVN_ERR(add_work_items(wcroot->sdb, b->work_items, scratch_pool));
8060 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
8061 b->conflict, scratch_pool));
8063 return SVN_NO_ERROR;
8066 static svn_error_t *
8067 op_delete_txn(void *baton,
8068 svn_wc__db_wcroot_t *wcroot,
8069 const char *local_relpath,
8070 apr_pool_t *scratch_pool)
8073 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8074 SVN_ERR(delete_node(baton, wcroot, local_relpath, scratch_pool));
8075 return SVN_NO_ERROR;
8079 struct op_delete_many_baton_t {
8080 apr_array_header_t *rel_targets;
8081 svn_boolean_t delete_dir_externals;
8082 const svn_skel_t *work_items;
8083 } op_delete_many_baton_t;
8085 static svn_error_t *
8086 op_delete_many_txn(void *baton,
8087 svn_wc__db_wcroot_t *wcroot,
8088 const char *local_relpath,
8089 apr_pool_t *scratch_pool)
8091 struct op_delete_many_baton_t *odmb = baton;
8092 struct op_delete_baton_t odb;
8094 apr_pool_t *iterpool;
8096 odb.moved_to_relpath = NULL;
8097 odb.conflict = NULL;
8098 odb.work_items = NULL;
8099 odb.delete_dir_externals = odmb->delete_dir_externals;
8102 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8103 iterpool = svn_pool_create(scratch_pool);
8104 for (i = 0; i < odmb->rel_targets->nelts; i++)
8106 const char *target_relpath = APR_ARRAY_IDX(odmb->rel_targets, i,
8110 svn_pool_clear(iterpool);
8111 SVN_ERR(delete_node(&odb, wcroot, target_relpath, iterpool));
8113 svn_pool_destroy(iterpool);
8115 SVN_ERR(add_work_items(wcroot->sdb, odmb->work_items, scratch_pool));
8117 return SVN_NO_ERROR;
8121 static svn_error_t *
8122 do_delete_notify(void *baton,
8123 svn_wc__db_wcroot_t *wcroot,
8124 svn_cancel_func_t cancel_func,
8126 svn_wc_notify_func2_t notify_func,
8128 apr_pool_t *scratch_pool)
8130 svn_sqlite__stmt_t *stmt;
8131 svn_boolean_t have_row;
8132 apr_pool_t *iterpool;
8134 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8135 STMT_SELECT_DELETE_LIST));
8136 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8138 iterpool = svn_pool_create(scratch_pool);
8141 const char *notify_relpath;
8142 const char *notify_abspath;
8144 svn_pool_clear(iterpool);
8146 notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8147 notify_abspath = svn_dirent_join(wcroot->abspath,
8151 notify_func(notify_baton,
8152 svn_wc_create_notify(notify_abspath,
8153 svn_wc_notify_delete,
8157 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8159 svn_pool_destroy(iterpool);
8161 SVN_ERR(svn_sqlite__reset(stmt));
8163 /* We only allow cancellation after notification for all deleted nodes
8164 * has happened. The nodes are already deleted so we should notify for
8167 SVN_ERR(cancel_func(cancel_baton));
8169 return SVN_NO_ERROR;
8174 svn_wc__db_op_delete(svn_wc__db_t *db,
8175 const char *local_abspath,
8176 const char *moved_to_abspath,
8177 svn_boolean_t delete_dir_externals,
8178 svn_skel_t *conflict,
8179 svn_skel_t *work_items,
8180 svn_cancel_func_t cancel_func,
8182 svn_wc_notify_func2_t notify_func,
8184 apr_pool_t *scratch_pool)
8186 svn_wc__db_wcroot_t *wcroot;
8187 svn_wc__db_wcroot_t *moved_to_wcroot;
8188 const char *local_relpath;
8189 const char *moved_to_relpath;
8190 struct op_delete_baton_t odb;
8192 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8194 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8196 scratch_pool, scratch_pool));
8197 VERIFY_USABLE_WCROOT(wcroot);
8199 if (moved_to_abspath)
8201 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&moved_to_wcroot,
8203 db, moved_to_abspath,
8206 VERIFY_USABLE_WCROOT(moved_to_wcroot);
8208 if (strcmp(wcroot->abspath, moved_to_wcroot->abspath) != 0)
8209 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
8210 _("Cannot move '%s' to '%s' because they "
8211 "are not in the same working copy"),
8212 svn_dirent_local_style(local_abspath,
8214 svn_dirent_local_style(moved_to_abspath,
8218 moved_to_relpath = NULL;
8220 odb.moved_to_relpath = moved_to_relpath;
8221 odb.conflict = conflict;
8222 odb.work_items = work_items;
8223 odb.delete_dir_externals = delete_dir_externals;
8227 /* Perform the deletion operation (transactionally), perform any
8228 notifications necessary, and then clean out our temporary tables. */
8230 SVN_ERR(with_finalization(wcroot, local_relpath,
8231 op_delete_txn, &odb,
8232 do_delete_notify, NULL,
8233 cancel_func, cancel_baton,
8234 notify_func, notify_baton,
8235 STMT_FINALIZE_DELETE,
8240 /* Avoid the trigger work */
8242 SVN_WC__DB_WITH_TXN(
8243 delete_node(&odb, wcroot, local_relpath, scratch_pool),
8247 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
8250 return SVN_NO_ERROR;
8255 svn_wc__db_op_delete_many(svn_wc__db_t *db,
8256 apr_array_header_t *targets,
8257 svn_boolean_t delete_dir_externals,
8258 const svn_skel_t *work_items,
8259 svn_cancel_func_t cancel_func,
8261 svn_wc_notify_func2_t notify_func,
8263 apr_pool_t *scratch_pool)
8265 svn_wc__db_wcroot_t *wcroot;
8266 const char *local_relpath;
8267 struct op_delete_many_baton_t odmb;
8269 apr_pool_t *iterpool;
8271 odmb.rel_targets = apr_array_make(scratch_pool, targets->nelts,
8272 sizeof(const char *));
8273 odmb.work_items = work_items;
8274 odmb.delete_dir_externals = delete_dir_externals;
8275 iterpool = svn_pool_create(scratch_pool);
8276 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8278 APR_ARRAY_IDX(targets, 0,
8280 scratch_pool, iterpool));
8281 VERIFY_USABLE_WCROOT(wcroot);
8282 for (i = 0; i < targets->nelts; i++)
8284 const char *local_abspath = APR_ARRAY_IDX(targets, i, const char*);
8285 svn_wc__db_wcroot_t *target_wcroot;
8287 svn_pool_clear(iterpool);
8289 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&target_wcroot,
8291 APR_ARRAY_IDX(targets, i,
8293 scratch_pool, iterpool));
8294 VERIFY_USABLE_WCROOT(target_wcroot);
8295 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8297 /* Assert that all targets are within the same working copy. */
8298 SVN_ERR_ASSERT(wcroot->wc_id == target_wcroot->wc_id);
8300 APR_ARRAY_PUSH(odmb.rel_targets, const char *) = local_relpath;
8301 SVN_ERR(flush_entries(target_wcroot, local_abspath, svn_depth_infinity,
8305 svn_pool_destroy(iterpool);
8307 /* Perform the deletion operation (transactionally), perform any
8308 notifications necessary, and then clean out our temporary tables. */
8309 return svn_error_trace(with_finalization(wcroot, wcroot->abspath,
8310 op_delete_many_txn, &odmb,
8311 do_delete_notify, NULL,
8312 cancel_func, cancel_baton,
8313 notify_func, notify_baton,
8314 STMT_FINALIZE_DELETE,
8319 /* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of
8320 DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */
8321 static svn_error_t *
8322 read_info(svn_wc__db_status_t *status,
8323 svn_node_kind_t *kind,
8324 svn_revnum_t *revision,
8325 const char **repos_relpath,
8326 apr_int64_t *repos_id,
8327 svn_revnum_t *changed_rev,
8328 apr_time_t *changed_date,
8329 const char **changed_author,
8331 const svn_checksum_t **checksum,
8332 const char **target,
8333 const char **original_repos_relpath,
8334 apr_int64_t *original_repos_id,
8335 svn_revnum_t *original_revision,
8336 svn_wc__db_lock_t **lock,
8337 svn_filesize_t *recorded_size,
8338 apr_time_t *recorded_time,
8339 const char **changelist,
8340 svn_boolean_t *conflicted,
8341 svn_boolean_t *op_root,
8342 svn_boolean_t *had_props,
8343 svn_boolean_t *props_mod,
8344 svn_boolean_t *have_base,
8345 svn_boolean_t *have_more_work,
8346 svn_boolean_t *have_work,
8347 svn_wc__db_wcroot_t *wcroot,
8348 const char *local_relpath,
8349 apr_pool_t *result_pool,
8350 apr_pool_t *scratch_pool)
8352 svn_sqlite__stmt_t *stmt_info;
8353 svn_sqlite__stmt_t *stmt_act;
8354 svn_boolean_t have_info;
8355 svn_boolean_t have_act;
8356 svn_error_t *err = NULL;
8358 /* Obtain the most likely to exist record first, to make sure we don't
8359 have to obtain the SQLite read-lock multiple times */
8360 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
8361 lock ? STMT_SELECT_NODE_INFO_WITH_LOCK
8362 : STMT_SELECT_NODE_INFO));
8363 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
8364 SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
8366 if (changelist || conflicted || props_mod)
8368 SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
8369 STMT_SELECT_ACTUAL_NODE));
8370 SVN_ERR(svn_sqlite__bindf(stmt_act, "is", wcroot->wc_id, local_relpath));
8371 SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
8382 svn_node_kind_t node_kind;
8384 op_depth = svn_sqlite__column_int(stmt_info, 0);
8385 node_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
8389 *status = svn_sqlite__column_token(stmt_info, 3, presence_map);
8391 if (op_depth != 0) /* WORKING */
8392 err = svn_error_compose_create(err,
8393 convert_to_working_status(status,
8403 *repos_id = INVALID_REPOS_ID;
8405 *revision = SVN_INVALID_REVNUM;
8407 /* Our path is implied by our parent somewhere up the tree.
8408 With the NULL value and status, the caller will know to
8409 search up the tree for the base of our path. */
8410 *repos_relpath = NULL;
8414 /* Fetch repository information. If we have a
8415 WORKING_NODE (and have been added), then the repository
8416 we're being added to will be dependent upon a parent. The
8417 caller can scan upwards to locate the repository. */
8418 repos_location_from_columns(repos_id, revision, repos_relpath,
8419 stmt_info, 1, 5, 2, result_pool);
8423 *changed_rev = svn_sqlite__column_revnum(stmt_info, 8);
8427 *changed_date = svn_sqlite__column_int64(stmt_info, 9);
8431 *changed_author = svn_sqlite__column_text(stmt_info, 10,
8436 *recorded_time = svn_sqlite__column_int64(stmt_info, 13);
8440 if (node_kind != svn_node_dir)
8442 *depth = svn_depth_unknown;
8446 *depth = svn_sqlite__column_token_null(stmt_info, 11, depth_map,
8452 if (node_kind != svn_node_file)
8459 err = svn_error_compose_create(
8460 err, svn_sqlite__column_checksum(checksum, stmt_info, 6,
8466 *recorded_size = get_recorded_size(stmt_info, 7);
8470 if (node_kind != svn_node_symlink)
8473 *target = svn_sqlite__column_text(stmt_info, 12, result_pool);
8478 *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8484 if (original_repos_id)
8485 *original_repos_id = INVALID_REPOS_ID;
8486 if (original_revision)
8487 *original_revision = SVN_INVALID_REVNUM;
8488 if (original_repos_relpath)
8489 *original_repos_relpath = NULL;
8493 repos_location_from_columns(original_repos_id,
8495 original_repos_relpath,
8496 stmt_info, 1, 5, 2, result_pool);
8500 *props_mod = have_act && !svn_sqlite__column_is_null(stmt_act, 1);
8504 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt_info, 14);
8511 !svn_sqlite__column_is_null(stmt_act, 2); /* conflict_data */
8514 *conflicted = FALSE;
8522 *lock = lock_from_columns(stmt_info, 17, 18, 19, 20, result_pool);
8526 *have_work = (op_depth != 0);
8530 *op_root = ((op_depth > 0)
8531 && (op_depth == relpath_depth(local_relpath)));
8534 if (have_base || have_more_work)
8537 *have_more_work = FALSE;
8539 while (!err && op_depth != 0)
8541 err = svn_sqlite__step(&have_info, stmt_info);
8543 if (err || !have_info)
8546 op_depth = svn_sqlite__column_int(stmt_info, 0);
8551 *have_more_work = TRUE;
8559 *have_base = (op_depth == 0);
8564 /* A row in ACTUAL_NODE should never exist without a corresponding
8565 node in BASE_NODE and/or WORKING_NODE unless it flags a tree conflict. */
8566 if (svn_sqlite__column_is_null(stmt_act, 2)) /* conflict_data */
8567 err = svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
8568 _("Corrupt data for '%s'"),
8569 path_for_error_message(wcroot, local_relpath,
8571 /* ### What should we return? Should we have a separate
8572 function for reading actual-only nodes? */
8574 /* As a safety measure, until we decide if we want to use
8575 read_info for actual-only nodes, make sure the caller asked
8576 for the conflict status. */
8577 SVN_ERR_ASSERT(conflicted);
8580 *status = svn_wc__db_status_normal; /* What! No it's not! */
8582 *kind = svn_node_unknown;
8584 *revision = SVN_INVALID_REVNUM;
8586 *repos_relpath = NULL;
8588 *repos_id = INVALID_REPOS_ID;
8590 *changed_rev = SVN_INVALID_REVNUM;
8594 *depth = svn_depth_unknown;
8599 if (original_repos_relpath)
8600 *original_repos_relpath = NULL;
8601 if (original_repos_id)
8602 *original_repos_id = INVALID_REPOS_ID;
8603 if (original_revision)
8604 *original_revision = SVN_INVALID_REVNUM;
8612 *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8624 *have_more_work = FALSE;
8630 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
8631 _("The node '%s' was not found."),
8632 path_for_error_message(wcroot, local_relpath,
8636 if (stmt_act != NULL)
8637 err = svn_error_compose_create(err, svn_sqlite__reset(stmt_act));
8639 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
8640 err = svn_error_quick_wrap(err,
8641 apr_psprintf(scratch_pool,
8642 "Error reading node '%s'",
8645 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info)));
8647 return SVN_NO_ERROR;
8652 svn_wc__db_read_info_internal(svn_wc__db_status_t *status,
8653 svn_node_kind_t *kind,
8654 svn_revnum_t *revision,
8655 const char **repos_relpath,
8656 apr_int64_t *repos_id,
8657 svn_revnum_t *changed_rev,
8658 apr_time_t *changed_date,
8659 const char **changed_author,
8661 const svn_checksum_t **checksum,
8662 const char **target,
8663 const char **original_repos_relpath,
8664 apr_int64_t *original_repos_id,
8665 svn_revnum_t *original_revision,
8666 svn_wc__db_lock_t **lock,
8667 svn_filesize_t *recorded_size,
8668 apr_time_t *recorded_time,
8669 const char **changelist,
8670 svn_boolean_t *conflicted,
8671 svn_boolean_t *op_root,
8672 svn_boolean_t *had_props,
8673 svn_boolean_t *props_mod,
8674 svn_boolean_t *have_base,
8675 svn_boolean_t *have_more_work,
8676 svn_boolean_t *have_work,
8677 svn_wc__db_wcroot_t *wcroot,
8678 const char *local_relpath,
8679 apr_pool_t *result_pool,
8680 apr_pool_t *scratch_pool)
8682 return svn_error_trace(
8683 read_info(status, kind, revision, repos_relpath, repos_id,
8684 changed_rev, changed_date, changed_author,
8685 depth, checksum, target, original_repos_relpath,
8686 original_repos_id, original_revision, lock,
8687 recorded_size, recorded_time, changelist, conflicted,
8688 op_root, had_props, props_mod,
8689 have_base, have_more_work, have_work,
8690 wcroot, local_relpath, result_pool, scratch_pool));
8695 svn_wc__db_read_info(svn_wc__db_status_t *status,
8696 svn_node_kind_t *kind,
8697 svn_revnum_t *revision,
8698 const char **repos_relpath,
8699 const char **repos_root_url,
8700 const char **repos_uuid,
8701 svn_revnum_t *changed_rev,
8702 apr_time_t *changed_date,
8703 const char **changed_author,
8705 const svn_checksum_t **checksum,
8706 const char **target,
8707 const char **original_repos_relpath,
8708 const char **original_root_url,
8709 const char **original_uuid,
8710 svn_revnum_t *original_revision,
8711 svn_wc__db_lock_t **lock,
8712 svn_filesize_t *recorded_size,
8713 apr_time_t *recorded_time,
8714 const char **changelist,
8715 svn_boolean_t *conflicted,
8716 svn_boolean_t *op_root,
8717 svn_boolean_t *have_props,
8718 svn_boolean_t *props_mod,
8719 svn_boolean_t *have_base,
8720 svn_boolean_t *have_more_work,
8721 svn_boolean_t *have_work,
8723 const char *local_abspath,
8724 apr_pool_t *result_pool,
8725 apr_pool_t *scratch_pool)
8727 svn_wc__db_wcroot_t *wcroot;
8728 const char *local_relpath;
8729 apr_int64_t repos_id, original_repos_id;
8731 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8733 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
8734 local_abspath, scratch_pool, scratch_pool));
8735 VERIFY_USABLE_WCROOT(wcroot);
8737 SVN_ERR(read_info(status, kind, revision, repos_relpath, &repos_id,
8738 changed_rev, changed_date, changed_author,
8739 depth, checksum, target, original_repos_relpath,
8740 &original_repos_id, original_revision, lock,
8741 recorded_size, recorded_time, changelist, conflicted,
8742 op_root, have_props, props_mod,
8743 have_base, have_more_work, have_work,
8744 wcroot, local_relpath, result_pool, scratch_pool));
8745 SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
8746 wcroot->sdb, repos_id, result_pool));
8747 SVN_ERR(svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
8748 wcroot->sdb, original_repos_id,
8751 return SVN_NO_ERROR;
8754 static svn_error_t *
8755 is_wclocked(svn_boolean_t *locked,
8756 svn_wc__db_wcroot_t *wcroot,
8757 const char *dir_relpath,
8758 apr_pool_t *scratch_pool);
8760 /* What we really want to store about a node. This relies on the
8761 offset of svn_wc__db_info_t being zero. */
8762 struct read_children_info_item_t
8764 struct svn_wc__db_info_t info;
8769 static svn_error_t *
8770 read_children_info(svn_wc__db_wcroot_t *wcroot,
8771 const char *dir_relpath,
8772 apr_hash_t *conflicts,
8774 apr_pool_t *result_pool,
8775 apr_pool_t *scratch_pool)
8777 svn_sqlite__stmt_t *stmt;
8778 svn_boolean_t have_row;
8779 const char *repos_root_url = NULL;
8780 const char *repos_uuid = NULL;
8781 apr_int64_t last_repos_id = INVALID_REPOS_ID;
8783 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8784 STMT_SELECT_NODE_CHILDREN_INFO));
8785 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
8786 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8790 /* CHILD item points to what we have about the node. We only provide
8791 CHILD->item to our caller. */
8792 struct read_children_info_item_t *child_item;
8793 const char *child_relpath = svn_sqlite__column_text(stmt, 19, NULL);
8794 const char *name = svn_relpath_basename(child_relpath, NULL);
8797 svn_boolean_t new_child;
8799 child_item = svn_hash_gets(nodes, name);
8804 child_item = apr_pcalloc(result_pool, sizeof(*child_item));
8808 op_depth = svn_sqlite__column_int(stmt, 0);
8810 /* Do we have new or better information? */
8811 if (new_child || op_depth > child_item->op_depth)
8813 struct svn_wc__db_info_t *child = &child_item->info;
8814 child_item->op_depth = op_depth;
8816 child->kind = svn_sqlite__column_token(stmt, 4, kind_map);
8818 child->status = svn_sqlite__column_token(stmt, 3, presence_map);
8821 if (child->status == svn_wc__db_status_incomplete)
8822 child->incomplete = TRUE;
8823 err = convert_to_working_status(&child->status, child->status);
8825 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8829 child->revnum = SVN_INVALID_REVNUM;
8831 child->revnum = svn_sqlite__column_revnum(stmt, 5);
8834 child->repos_relpath = NULL;
8836 child->repos_relpath = svn_sqlite__column_text(stmt, 2,
8839 if (op_depth != 0 || svn_sqlite__column_is_null(stmt, 1))
8841 child->repos_root_url = NULL;
8842 child->repos_uuid = NULL;
8846 const char *last_repos_root_url = NULL;
8848 apr_int64_t repos_id = svn_sqlite__column_int64(stmt, 1);
8849 if (!repos_root_url ||
8850 (last_repos_id != INVALID_REPOS_ID &&
8851 repos_id != last_repos_id))
8853 last_repos_root_url = repos_root_url;
8854 err = svn_wc__db_fetch_repos_info(&repos_root_url,
8856 wcroot->sdb, repos_id,
8859 SVN_ERR(svn_error_compose_create(err,
8860 svn_sqlite__reset(stmt)));
8863 if (last_repos_id == INVALID_REPOS_ID)
8864 last_repos_id = repos_id;
8866 /* Assume working copy is all one repos_id so that a
8867 single cached value is sufficient. */
8868 if (repos_id != last_repos_id)
8870 err= svn_error_createf(
8871 SVN_ERR_WC_DB_ERROR, NULL,
8872 _("The node '%s' comes from unexpected repository "
8873 "'%s', expected '%s'; if this node is a file "
8874 "external using the correct URL in the external "
8875 "definition can fix the problem, see issue #4087"),
8876 child_relpath, repos_root_url, last_repos_root_url);
8877 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
8879 child->repos_root_url = repos_root_url;
8880 child->repos_uuid = repos_uuid;
8883 child->changed_rev = svn_sqlite__column_revnum(stmt, 8);
8885 child->changed_date = svn_sqlite__column_int64(stmt, 9);
8887 child->changed_author = svn_sqlite__column_text(stmt, 10,
8890 if (child->kind != svn_node_dir)
8891 child->depth = svn_depth_unknown;
8894 child->depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
8897 SVN_ERR(is_wclocked(&child->locked, wcroot, child_relpath,
8901 child->recorded_time = svn_sqlite__column_int64(stmt, 13);
8902 child->recorded_size = get_recorded_size(stmt, 7);
8903 child->has_checksum = !svn_sqlite__column_is_null(stmt, 6);
8904 child->copied = op_depth > 0 && !svn_sqlite__column_is_null(stmt, 2);
8905 child->had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
8907 if (child->had_props)
8909 apr_hash_t *properties;
8910 err = svn_sqlite__column_properties(&properties, stmt, 14,
8911 scratch_pool, scratch_pool);
8913 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8915 child->special = (child->had_props
8916 && svn_hash_gets(properties, SVN_PROP_SPECIAL));
8920 child->op_root = FALSE;
8922 child->op_root = (op_depth == relpath_depth(child_relpath));
8924 if (op_depth && child->op_root)
8925 child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
8928 svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
8933 child_item->info.have_base = TRUE;
8935 /* Get the lock info, available only at op_depth 0. */
8936 child_item->info.lock = lock_from_columns(stmt, 15, 16, 17, 18,
8939 /* FILE_EXTERNAL flag only on op_depth 0. */
8940 child_item->info.file_external = svn_sqlite__column_boolean(stmt,
8945 const char *moved_to_relpath;
8947 child_item->nr_layers++;
8948 child_item->info.have_more_work = (child_item->nr_layers > 1);
8951 /* A local_relpath can be moved multiple times at different op
8952 depths and it really depends on the caller what is interesting.
8953 We provide a simple linked list with the moved_from information */
8955 moved_to_relpath = svn_sqlite__column_text(stmt, 21, NULL);
8956 if (moved_to_relpath)
8958 struct svn_wc__db_moved_to_info_t *moved_to;
8959 struct svn_wc__db_moved_to_info_t **next;
8960 const char *shadow_op_relpath;
8963 moved_to = apr_pcalloc(result_pool, sizeof(*moved_to));
8964 moved_to->moved_to_abspath = svn_dirent_join(wcroot->abspath,
8968 cur_op_depth = relpath_depth(child_relpath);
8969 shadow_op_relpath = child_relpath;
8971 while (cur_op_depth > op_depth)
8973 shadow_op_relpath = svn_relpath_dirname(shadow_op_relpath,
8978 moved_to->shadow_op_root_abspath =
8979 svn_dirent_join(wcroot->abspath, shadow_op_relpath,
8982 next = &child_item->info.moved_to;
8985 0 < strcmp((*next)->shadow_op_root_abspath,
8986 moved_to->shadow_op_root_abspath))
8987 next = &((*next)->next);
8989 moved_to->next = *next;
8994 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8997 SVN_ERR(svn_sqlite__reset(stmt));
8999 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9000 STMT_SELECT_ACTUAL_CHILDREN_INFO));
9001 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9002 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9006 struct read_children_info_item_t *child_item;
9007 struct svn_wc__db_info_t *child;
9008 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9009 const char *name = svn_relpath_basename(child_relpath, NULL);
9011 child_item = svn_hash_gets(nodes, name);
9014 child_item = apr_pcalloc(result_pool, sizeof(*child_item));
9015 child_item->info.status = svn_wc__db_status_not_present;
9018 child = &child_item->info;
9020 child->changelist = svn_sqlite__column_text(stmt, 1, result_pool);
9022 child->props_mod = !svn_sqlite__column_is_null(stmt, 2);
9024 if (child->props_mod)
9027 apr_hash_t *properties;
9029 err = svn_sqlite__column_properties(&properties, stmt, 2,
9030 scratch_pool, scratch_pool);
9032 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9033 child->special = (NULL != svn_hash_gets(properties,
9038 child->conflicted = !svn_sqlite__column_is_null(stmt, 3); /* conflict */
9040 if (child->conflicted)
9041 svn_hash_sets(conflicts, apr_pstrdup(result_pool, name), "");
9043 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9046 SVN_ERR(svn_sqlite__reset(stmt));
9048 return SVN_NO_ERROR;
9052 svn_wc__db_read_children_info(apr_hash_t **nodes,
9053 apr_hash_t **conflicts,
9055 const char *dir_abspath,
9056 apr_pool_t *result_pool,
9057 apr_pool_t *scratch_pool)
9059 svn_wc__db_wcroot_t *wcroot;
9060 const char *dir_relpath;
9062 *conflicts = apr_hash_make(result_pool);
9063 *nodes = apr_hash_make(result_pool);
9064 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9066 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9068 scratch_pool, scratch_pool));
9069 VERIFY_USABLE_WCROOT(wcroot);
9071 SVN_WC__DB_WITH_TXN(
9072 read_children_info(wcroot, dir_relpath, *conflicts, *nodes,
9073 result_pool, scratch_pool),
9076 return SVN_NO_ERROR;
9079 static svn_error_t *
9080 db_read_props(apr_hash_t **props,
9081 svn_wc__db_wcroot_t *wcroot,
9082 const char *local_relpath,
9083 apr_pool_t *result_pool,
9084 apr_pool_t *scratch_pool);
9086 static svn_error_t *
9087 read_single_info(const struct svn_wc__db_info_t **info,
9088 svn_wc__db_wcroot_t *wcroot,
9089 const char *local_relpath,
9090 apr_pool_t *result_pool,
9091 apr_pool_t *scratch_pool)
9093 struct svn_wc__db_info_t *mtb;
9094 apr_int64_t repos_id;
9095 const svn_checksum_t *checksum;
9096 const char *original_repos_relpath;
9097 svn_boolean_t have_work;
9099 mtb = apr_pcalloc(result_pool, sizeof(*mtb));
9101 SVN_ERR(read_info(&mtb->status, &mtb->kind, &mtb->revnum,
9102 &mtb->repos_relpath, &repos_id, &mtb->changed_rev,
9103 &mtb->changed_date, &mtb->changed_author, &mtb->depth,
9104 &checksum, NULL, &original_repos_relpath, NULL, NULL,
9105 &mtb->lock, &mtb->recorded_size, &mtb->recorded_time,
9106 &mtb->changelist, &mtb->conflicted, &mtb->op_root,
9107 &mtb->had_props, &mtb->props_mod, &mtb->have_base,
9108 &mtb->have_more_work, &have_work,
9109 wcroot, local_relpath,
9110 result_pool, scratch_pool));
9112 /* Query the same rows in the database again for move information */
9113 if (have_work && (mtb->have_base || mtb->have_more_work))
9115 svn_sqlite__stmt_t *stmt;
9116 svn_boolean_t have_row;
9117 const char *cur_relpath = NULL;
9120 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9121 STMT_SELECT_MOVED_TO_NODE));
9122 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9124 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9128 struct svn_wc__db_moved_to_info_t *move;
9129 int op_depth = svn_sqlite__column_int(stmt, 0);
9130 const char *moved_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
9132 move = apr_pcalloc(result_pool, sizeof(*move));
9133 move->moved_to_abspath = svn_dirent_join(wcroot->abspath,
9139 cur_relpath = local_relpath;
9140 cur_op_depth = relpath_depth(cur_relpath);
9142 while (cur_op_depth > op_depth)
9144 cur_relpath = svn_relpath_dirname(cur_relpath, scratch_pool);
9147 move->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath,
9151 move->next = mtb->moved_to;
9152 mtb->moved_to = move;
9154 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9157 SVN_ERR(svn_sqlite__reset(stmt));
9160 /* Maybe we have to get some shadowed lock from BASE to make our test suite
9161 happy... (It might be completely unrelated, but...)
9162 This queries the same BASE row again, joined to the lock table */
9163 if (mtb->have_base && (have_work || mtb->kind == svn_node_file))
9165 svn_boolean_t update_root;
9166 svn_wc__db_lock_t **lock_arg = NULL;
9169 lock_arg = &mtb->lock;
9171 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL, NULL,
9172 NULL, NULL, NULL, NULL, NULL,
9173 NULL, lock_arg, NULL, NULL,
9175 wcroot, local_relpath,
9176 result_pool, scratch_pool));
9178 mtb->file_external = (update_root && mtb->kind == svn_node_file);
9181 if (mtb->status == svn_wc__db_status_added)
9183 svn_wc__db_status_t status;
9185 SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9187 wcroot, local_relpath,
9188 result_pool, scratch_pool));
9190 mtb->moved_here = (status == svn_wc__db_status_moved_here);
9191 mtb->incomplete = (status == svn_wc__db_status_incomplete);
9195 if (mtb->kind == svn_node_file
9196 && (mtb->had_props || mtb->props_mod))
9198 apr_hash_t *properties;
9201 SVN_ERR(db_read_props(&properties,
9202 wcroot, local_relpath,
9203 scratch_pool, scratch_pool));
9205 SVN_ERR(db_read_pristine_props(&properties, wcroot, local_relpath,
9206 TRUE /* deleted_ok */,
9207 scratch_pool, scratch_pool));
9209 mtb->special = (NULL != svn_hash_gets(properties, SVN_PROP_SPECIAL));
9213 mtb->has_checksum = (checksum != NULL);
9214 mtb->copied = (original_repos_relpath != NULL);
9216 SVN_ERR(svn_wc__db_fetch_repos_info(&mtb->repos_root_url, &mtb->repos_uuid,
9217 wcroot->sdb, repos_id, result_pool));
9219 if (mtb->kind == svn_node_dir)
9220 SVN_ERR(is_wclocked(&mtb->locked, wcroot, local_relpath, scratch_pool));
9224 return SVN_NO_ERROR;
9228 svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info,
9230 const char *local_abspath,
9231 apr_pool_t *result_pool,
9232 apr_pool_t *scratch_pool)
9234 svn_wc__db_wcroot_t *wcroot;
9235 const char *local_relpath;
9237 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9239 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9241 scratch_pool, scratch_pool));
9242 VERIFY_USABLE_WCROOT(wcroot);
9244 SVN_WC__DB_WITH_TXN(read_single_info(info, wcroot, local_relpath,
9245 result_pool, scratch_pool),
9248 return SVN_NO_ERROR;
9252 svn_wc__db_read_pristine_info(svn_wc__db_status_t *status,
9253 svn_node_kind_t *kind,
9254 svn_revnum_t *changed_rev,
9255 apr_time_t *changed_date,
9256 const char **changed_author,
9257 svn_depth_t *depth, /* dirs only */
9258 const svn_checksum_t **checksum, /* files only */
9259 const char **target, /* symlinks only */
9260 svn_boolean_t *had_props,
9263 const char *local_abspath,
9264 apr_pool_t *result_pool,
9265 apr_pool_t *scratch_pool)
9267 svn_wc__db_wcroot_t *wcroot;
9268 const char *local_relpath;
9269 svn_sqlite__stmt_t *stmt;
9270 svn_boolean_t have_row;
9271 svn_error_t *err = NULL;
9273 svn_wc__db_status_t raw_status;
9274 svn_node_kind_t node_kind;
9276 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9278 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9280 scratch_pool, scratch_pool));
9281 VERIFY_USABLE_WCROOT(wcroot);
9283 /* Obtain the most likely to exist record first, to make sure we don't
9284 have to obtain the SQLite read-lock multiple times */
9285 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9286 STMT_SELECT_NODE_INFO));
9287 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9288 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9292 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9293 svn_sqlite__reset(stmt),
9294 _("The node '%s' was not found."),
9295 path_for_error_message(wcroot,
9300 op_depth = svn_sqlite__column_int(stmt, 0);
9301 raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9303 if (op_depth > 0 && raw_status == svn_wc__db_status_base_deleted)
9305 SVN_ERR(svn_sqlite__step_row(stmt));
9307 op_depth = svn_sqlite__column_int(stmt, 0);
9308 raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9311 node_kind = svn_sqlite__column_token(stmt, 4, kind_map);
9317 err = svn_error_compose_create(err,
9318 convert_to_working_status(
9323 *status = raw_status;
9331 *changed_rev = svn_sqlite__column_revnum(stmt, 8);
9335 *changed_date = svn_sqlite__column_int64(stmt, 9);
9339 *changed_author = svn_sqlite__column_text(stmt, 10,
9344 if (node_kind != svn_node_dir)
9346 *depth = svn_depth_unknown;
9350 *depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
9356 if (node_kind != svn_node_file)
9363 err2 = svn_sqlite__column_checksum(checksum, stmt, 6, result_pool);
9368 err = svn_error_compose_create(
9372 _("The node '%s' has a corrupt checksum value."),
9373 path_for_error_message(wcroot, local_relpath,
9382 if (node_kind != svn_node_symlink)
9385 *target = svn_sqlite__column_text(stmt, 12, result_pool);
9389 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
9393 if (raw_status == svn_wc__db_status_normal
9394 || raw_status == svn_wc__db_status_incomplete)
9396 SVN_ERR(svn_sqlite__column_properties(props, stmt, 14,
9397 result_pool, scratch_pool));
9399 *props = apr_hash_make(result_pool);
9403 assert(svn_sqlite__column_is_null(stmt, 14));
9408 return svn_error_trace(
9409 svn_error_compose_create(err,
9410 svn_sqlite__reset(stmt)));
9414 svn_wc__db_read_children_walker_info(apr_hash_t **nodes,
9416 const char *dir_abspath,
9417 apr_pool_t *result_pool,
9418 apr_pool_t *scratch_pool)
9420 svn_wc__db_wcroot_t *wcroot;
9421 const char *dir_relpath;
9422 svn_sqlite__stmt_t *stmt;
9423 svn_boolean_t have_row;
9425 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9427 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9429 scratch_pool, scratch_pool));
9430 VERIFY_USABLE_WCROOT(wcroot);
9432 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9433 STMT_SELECT_NODE_CHILDREN_WALKER_INFO));
9434 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9435 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9437 *nodes = apr_hash_make(result_pool);
9440 struct svn_wc__db_walker_info_t *child;
9441 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9442 const char *name = svn_relpath_basename(child_relpath, NULL);
9443 int op_depth = svn_sqlite__column_int(stmt, 1);
9446 child = apr_palloc(result_pool, sizeof(*child));
9447 child->status = svn_sqlite__column_token(stmt, 2, presence_map);
9450 err = convert_to_working_status(&child->status, child->status);
9452 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9454 child->kind = svn_sqlite__column_token(stmt, 3, kind_map);
9455 svn_hash_sets(*nodes, apr_pstrdup(result_pool, name), child);
9457 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9460 SVN_ERR(svn_sqlite__reset(stmt));
9462 return SVN_NO_ERROR;
9466 svn_wc__db_read_node_install_info(const char **wcroot_abspath,
9467 const svn_checksum_t **sha1_checksum,
9468 apr_hash_t **pristine_props,
9469 apr_time_t *changed_date,
9471 const char *local_abspath,
9472 const char *wri_abspath,
9473 apr_pool_t *result_pool,
9474 apr_pool_t *scratch_pool)
9476 svn_wc__db_wcroot_t *wcroot;
9477 const char *local_relpath;
9478 svn_sqlite__stmt_t *stmt;
9479 svn_error_t *err = NULL;
9480 svn_boolean_t have_row;
9482 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9485 wri_abspath = local_abspath;
9487 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9488 wri_abspath, scratch_pool, scratch_pool));
9489 VERIFY_USABLE_WCROOT(wcroot);
9491 if (local_abspath != wri_abspath
9492 && strcmp(local_abspath, wri_abspath))
9494 if (!svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
9495 return svn_error_createf(
9496 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
9497 _("The node '%s' is not in working copy '%s'"),
9498 svn_dirent_local_style(local_abspath, scratch_pool),
9499 svn_dirent_local_style(wcroot->abspath, scratch_pool));
9501 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
9504 if (wcroot_abspath != NULL)
9505 *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
9507 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9508 STMT_SELECT_NODE_INFO));
9510 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9512 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9516 if (!err && sha1_checksum)
9517 err = svn_sqlite__column_checksum(sha1_checksum, stmt, 6, result_pool);
9519 if (!err && pristine_props)
9521 err = svn_sqlite__column_properties(pristine_props, stmt, 14,
9522 result_pool, scratch_pool);
9523 /* Null means no props (assuming presence normal or incomplete). */
9524 if (*pristine_props == NULL)
9525 *pristine_props = apr_hash_make(result_pool);
9529 *changed_date = svn_sqlite__column_int64(stmt, 9);
9532 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9533 svn_sqlite__reset(stmt),
9534 _("The node '%s' is not installable"),
9535 svn_dirent_local_style(local_abspath,
9538 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9540 return SVN_NO_ERROR;
9545 /* The body of svn_wc__db_read_url().
9547 static svn_error_t *
9548 read_url_txn(const char **url,
9549 svn_wc__db_wcroot_t *wcroot,
9550 const char *local_relpath,
9551 apr_pool_t *result_pool,
9552 apr_pool_t *scratch_pool)
9554 svn_wc__db_status_t status;
9555 const char *repos_relpath;
9556 const char *repos_root_url;
9557 apr_int64_t repos_id;
9558 svn_boolean_t have_base;
9560 SVN_ERR(read_info(&status, NULL, NULL, &repos_relpath, &repos_id, NULL,
9561 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9562 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9563 &have_base, NULL, NULL,
9564 wcroot, local_relpath, scratch_pool, scratch_pool));
9566 if (repos_relpath == NULL)
9568 if (status == svn_wc__db_status_added)
9570 SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id, NULL,
9571 NULL, NULL, NULL, NULL, NULL,
9572 wcroot, local_relpath,
9573 scratch_pool, scratch_pool));
9575 else if (status == svn_wc__db_status_deleted)
9577 const char *base_del_relpath;
9578 const char *work_del_relpath;
9580 SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
9587 if (base_del_relpath)
9589 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
9594 NULL, NULL, NULL, NULL,
9600 repos_relpath = svn_relpath_join(
9602 svn_dirent_skip_ancestor(base_del_relpath,
9608 /* The parent of the WORKING delete, must be an addition */
9609 const char *work_relpath = NULL;
9611 /* work_del_relpath should not be NULL. However, we have
9612 * observed instances where that assumption was not met.
9613 * Bail out in that case instead of crashing with a segfault.
9615 SVN_ERR_ASSERT(work_del_relpath != NULL);
9616 work_relpath = svn_relpath_dirname(work_del_relpath,
9619 SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id,
9620 NULL, NULL, NULL, NULL, NULL, NULL,
9621 wcroot, work_relpath,
9622 scratch_pool, scratch_pool));
9624 repos_relpath = svn_relpath_join(
9626 svn_dirent_skip_ancestor(work_relpath,
9631 else if (status == svn_wc__db_status_excluded)
9633 const char *parent_relpath;
9637 /* Set 'url' to the *full URL* of the parent WC dir,
9638 * and 'name' to the *single path component* that is the
9639 * basename of this WC directory, so that joining them will result
9640 * in the correct full URL. */
9641 svn_relpath_split(&parent_relpath, &name, local_relpath,
9643 SVN_ERR(read_url_txn(&url2, wcroot, parent_relpath,
9644 scratch_pool, scratch_pool));
9646 *url = svn_path_url_add_component2(url2, name, result_pool);
9648 return SVN_NO_ERROR;
9652 /* All working statee are explicitly handled and all base statee
9653 have a repos_relpath */
9654 SVN_ERR_MALFUNCTION();
9658 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot->sdb,
9659 repos_id, scratch_pool));
9661 SVN_ERR_ASSERT(repos_root_url != NULL && repos_relpath != NULL);
9662 *url = svn_path_url_add_component2(repos_root_url, repos_relpath,
9665 return SVN_NO_ERROR;
9670 svn_wc__db_read_url(const char **url,
9672 const char *local_abspath,
9673 apr_pool_t *result_pool,
9674 apr_pool_t *scratch_pool)
9676 svn_wc__db_wcroot_t *wcroot;
9677 const char *local_relpath;
9679 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9681 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9683 scratch_pool, scratch_pool));
9684 VERIFY_USABLE_WCROOT(wcroot);
9686 SVN_WC__DB_WITH_TXN(read_url_txn(url, wcroot, local_relpath,
9687 result_pool, scratch_pool),
9690 return SVN_NO_ERROR;
9694 /* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
9695 a hash table mapping <tt>char *</tt> names onto svn_string_t *
9696 values for any properties of immediate or recursive child nodes of
9697 LOCAL_ABSPATH, the actual query being determined by STMT_IDX.
9698 If FILES_ONLY is true, only report properties for file child nodes.
9699 Check for cancellation between calls of RECEIVER_FUNC.
9701 typedef struct cache_props_baton_t
9704 svn_boolean_t pristine;
9705 const apr_array_header_t *changelists;
9706 svn_cancel_func_t cancel_func;
9708 } cache_props_baton_t;
9711 static svn_error_t *
9712 cache_props_recursive(void *cb_baton,
9713 svn_wc__db_wcroot_t *wcroot,
9714 const char *local_relpath,
9715 apr_pool_t *scratch_pool)
9717 cache_props_baton_t *baton = cb_baton;
9718 svn_sqlite__stmt_t *stmt;
9721 SVN_ERR(populate_targets_tree(wcroot, local_relpath, baton->depth,
9722 baton->changelists, scratch_pool));
9724 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
9725 STMT_CREATE_TARGET_PROP_CACHE));
9727 if (baton->pristine)
9728 stmt_idx = STMT_CACHE_TARGET_PRISTINE_PROPS;
9730 stmt_idx = STMT_CACHE_TARGET_PROPS;
9732 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
9733 SVN_ERR(svn_sqlite__bind_int64(stmt, 1, wcroot->wc_id));
9734 SVN_ERR(svn_sqlite__step_done(stmt));
9736 return SVN_NO_ERROR;
9741 svn_wc__db_read_props_streamily(svn_wc__db_t *db,
9742 const char *local_abspath,
9744 svn_boolean_t pristine,
9745 const apr_array_header_t *changelists,
9746 svn_wc__proplist_receiver_t receiver_func,
9747 void *receiver_baton,
9748 svn_cancel_func_t cancel_func,
9750 apr_pool_t *scratch_pool)
9752 svn_wc__db_wcroot_t *wcroot;
9753 const char *local_relpath;
9754 svn_sqlite__stmt_t *stmt;
9755 cache_props_baton_t baton;
9756 svn_boolean_t have_row;
9757 apr_pool_t *iterpool;
9758 svn_error_t *err = NULL;
9760 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9761 SVN_ERR_ASSERT(receiver_func);
9762 SVN_ERR_ASSERT((depth == svn_depth_files) ||
9763 (depth == svn_depth_immediates) ||
9764 (depth == svn_depth_infinity));
9766 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
9768 scratch_pool, scratch_pool));
9769 VERIFY_USABLE_WCROOT(wcroot);
9771 baton.depth = depth;
9772 baton.pristine = pristine;
9773 baton.changelists = changelists;
9774 baton.cancel_func = cancel_func;
9775 baton.cancel_baton = cancel_baton;
9777 SVN_ERR(with_finalization(wcroot, local_relpath,
9778 cache_props_recursive, &baton,
9780 cancel_func, cancel_baton,
9782 STMT_DROP_TARGETS_LIST,
9785 iterpool = svn_pool_create(scratch_pool);
9787 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9788 STMT_SELECT_ALL_TARGET_PROP_CACHE));
9789 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9790 while (!err && have_row)
9794 svn_pool_clear(iterpool);
9796 SVN_ERR(svn_sqlite__column_properties(&props, stmt, 1, iterpool,
9799 /* see if someone wants to cancel this operation. */
9801 err = cancel_func(cancel_baton);
9803 if (!err && props && apr_hash_count(props) != 0)
9805 const char *child_relpath;
9806 const char *child_abspath;
9808 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9809 child_abspath = svn_dirent_join(wcroot->abspath,
9810 child_relpath, iterpool);
9812 err = receiver_func(receiver_baton, child_abspath, props, iterpool);
9815 err = svn_error_compose_create(err, svn_sqlite__step(&have_row, stmt));
9818 err = svn_error_compose_create(err, svn_sqlite__reset(stmt));
9820 svn_pool_destroy(iterpool);
9822 SVN_ERR(svn_error_compose_create(
9824 svn_sqlite__exec_statements(wcroot->sdb,
9825 STMT_DROP_TARGET_PROP_CACHE)));
9826 return SVN_NO_ERROR;
9830 /* Helper for svn_wc__db_read_props().
9832 static svn_error_t *
9833 db_read_props(apr_hash_t **props,
9834 svn_wc__db_wcroot_t *wcroot,
9835 const char *local_relpath,
9836 apr_pool_t *result_pool,
9837 apr_pool_t *scratch_pool)
9839 svn_sqlite__stmt_t *stmt;
9840 svn_boolean_t have_row;
9841 svn_error_t *err = NULL;
9843 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9844 STMT_SELECT_ACTUAL_PROPS));
9845 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9846 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9848 if (have_row && !svn_sqlite__column_is_null(stmt, 0))
9850 err = svn_sqlite__column_properties(props, stmt, 0,
9851 result_pool, scratch_pool);
9856 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9859 return SVN_NO_ERROR;
9861 /* No local changes. Return the pristine props for this node. */
9862 SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, FALSE,
9863 result_pool, scratch_pool));
9866 /* Pristine properties are not defined for this node.
9867 ### we need to determine whether this node is in a state that
9868 ### allows for ACTUAL properties (ie. not deleted). for now,
9869 ### just say all nodes, no matter the state, have at least an
9870 ### empty set of props. */
9871 *props = apr_hash_make(result_pool);
9874 return SVN_NO_ERROR;
9879 svn_wc__db_read_props(apr_hash_t **props,
9881 const char *local_abspath,
9882 apr_pool_t *result_pool,
9883 apr_pool_t *scratch_pool)
9885 svn_wc__db_wcroot_t *wcroot;
9886 const char *local_relpath;
9888 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9890 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9891 local_abspath, scratch_pool, scratch_pool));
9892 VERIFY_USABLE_WCROOT(wcroot);
9894 SVN_WC__DB_WITH_TXN(db_read_props(props, wcroot, local_relpath,
9895 result_pool, scratch_pool),
9898 return SVN_NO_ERROR;
9902 static svn_error_t *
9903 db_read_pristine_props(apr_hash_t **props,
9904 svn_wc__db_wcroot_t *wcroot,
9905 const char *local_relpath,
9906 svn_boolean_t deleted_ok,
9907 apr_pool_t *result_pool,
9908 apr_pool_t *scratch_pool)
9910 svn_sqlite__stmt_t *stmt;
9911 svn_boolean_t have_row;
9912 svn_wc__db_status_t presence;
9914 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_NODE_PROPS));
9915 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9917 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9921 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9922 svn_sqlite__reset(stmt),
9923 _("The node '%s' was not found."),
9924 path_for_error_message(wcroot,
9930 /* Examine the presence: */
9931 presence = svn_sqlite__column_token(stmt, 1, presence_map);
9933 /* For "base-deleted", it is obvious the pristine props are located
9934 below the current node. Fetch the NODE from the next record. */
9935 if (presence == svn_wc__db_status_base_deleted && deleted_ok)
9937 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9939 SVN_ERR_ASSERT(have_row);
9941 presence = svn_sqlite__column_token(stmt, 1, presence_map);
9944 /* normal or copied: Fetch properties (during update we want
9945 properties for incomplete as well) */
9946 if (presence == svn_wc__db_status_normal
9947 || presence == svn_wc__db_status_incomplete)
9951 err = svn_sqlite__column_properties(props, stmt, 0, result_pool,
9953 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9956 *props = apr_hash_make(result_pool);
9958 return SVN_NO_ERROR;
9961 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
9962 svn_sqlite__reset(stmt),
9963 _("The node '%s' has a status that"
9964 " has no properties."),
9965 path_for_error_message(wcroot,
9972 svn_wc__db_read_pristine_props(apr_hash_t **props,
9974 const char *local_abspath,
9975 apr_pool_t *result_pool,
9976 apr_pool_t *scratch_pool)
9978 svn_wc__db_wcroot_t *wcroot;
9979 const char *local_relpath;
9981 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9983 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9984 local_abspath, scratch_pool, scratch_pool));
9985 VERIFY_USABLE_WCROOT(wcroot);
9987 SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, TRUE,
9988 result_pool, scratch_pool));
9989 return SVN_NO_ERROR;
9993 svn_wc__db_prop_retrieve_recursive(apr_hash_t **values,
9995 const char *local_abspath,
9996 const char *propname,
9997 apr_pool_t *result_pool,
9998 apr_pool_t *scratch_pool)
10000 svn_wc__db_wcroot_t *wcroot;
10001 const char *local_relpath;
10002 svn_sqlite__stmt_t *stmt;
10003 svn_boolean_t have_row;
10004 apr_pool_t *iterpool;
10006 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10008 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10009 local_abspath, scratch_pool, scratch_pool));
10010 VERIFY_USABLE_WCROOT(wcroot);
10012 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10013 STMT_SELECT_CURRENT_PROPS_RECURSIVE));
10014 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10016 *values = apr_hash_make(result_pool);
10018 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10019 iterpool = svn_pool_create(scratch_pool);
10022 apr_hash_t *node_props;
10023 svn_string_t *value;
10025 svn_pool_clear(iterpool);
10027 SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 0,
10028 iterpool, iterpool));
10030 value = (node_props
10031 ? svn_hash_gets(node_props, propname)
10036 svn_hash_sets(*values,
10037 svn_dirent_join(wcroot->abspath,
10038 svn_sqlite__column_text(stmt, 1, NULL),
10040 svn_string_dup(value, result_pool));
10043 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10046 svn_pool_destroy(iterpool);
10048 return svn_error_trace(svn_sqlite__reset(stmt));
10051 /* The body of svn_wc__db_read_cached_iprops(). */
10052 static svn_error_t *
10053 db_read_cached_iprops(apr_array_header_t **iprops,
10054 svn_wc__db_wcroot_t *wcroot,
10055 const char *local_relpath,
10056 apr_pool_t *result_pool,
10057 apr_pool_t *scratch_pool)
10059 svn_sqlite__stmt_t *stmt;
10060 svn_boolean_t have_row;
10062 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_IPROPS));
10063 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10064 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10068 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10069 svn_sqlite__reset(stmt),
10070 _("The node '%s' was not found."),
10071 path_for_error_message(wcroot, local_relpath,
10075 SVN_ERR(svn_sqlite__column_iprops(iprops, stmt, 0,
10076 result_pool, scratch_pool));
10078 SVN_ERR(svn_sqlite__reset(stmt));
10080 return SVN_NO_ERROR;
10084 svn_wc__db_read_cached_iprops(apr_array_header_t **iprops,
10086 const char *local_abspath,
10087 apr_pool_t *result_pool,
10088 apr_pool_t *scratch_pool)
10090 svn_wc__db_wcroot_t *wcroot;
10091 const char *local_relpath;
10093 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10095 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10097 scratch_pool, scratch_pool));
10098 VERIFY_USABLE_WCROOT(wcroot);
10100 /* Don't use with_txn yet, as we perform just a single transaction */
10101 SVN_ERR(db_read_cached_iprops(iprops, wcroot, local_relpath,
10102 result_pool, scratch_pool));
10106 *iprops = apr_array_make(result_pool, 0,
10107 sizeof(svn_prop_inherited_item_t *));
10110 return SVN_NO_ERROR;
10113 /* Remove all prop name value pairs from PROP_HASH where the property
10114 name is not PROPNAME. */
10116 filter_unwanted_props(apr_hash_t *prop_hash,
10117 const char * propname,
10118 apr_pool_t *scratch_pool)
10120 apr_hash_index_t *hi;
10122 for (hi = apr_hash_first(scratch_pool, prop_hash);
10124 hi = apr_hash_next(hi))
10126 const char *ipropname = svn__apr_hash_index_key(hi);
10128 if (strcmp(ipropname, propname) != 0)
10129 svn_hash_sets(prop_hash, ipropname, NULL);
10134 /* Get the changed properties as stored in the ACTUAL table */
10135 static svn_error_t *
10136 db_get_changed_props(apr_hash_t **actual_props,
10137 svn_wc__db_wcroot_t *wcroot,
10138 const char *local_relpath,
10139 apr_pool_t *result_pool,
10140 apr_pool_t *scratch_pool)
10142 svn_sqlite__stmt_t *stmt;
10143 svn_boolean_t have_row;
10144 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10145 STMT_SELECT_ACTUAL_PROPS));
10146 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10147 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10149 if (have_row && !svn_sqlite__column_is_null(stmt, 0))
10150 SVN_ERR(svn_sqlite__column_properties(actual_props, stmt, 0,
10151 result_pool, scratch_pool));
10153 *actual_props = NULL; /* Cached when we read that record */
10155 return svn_error_trace(svn_sqlite__reset(stmt));
10158 /* The body of svn_wc__db_read_inherited_props(). */
10159 static svn_error_t *
10160 db_read_inherited_props(apr_array_header_t **inherited_props,
10161 apr_hash_t **actual_props,
10162 svn_wc__db_wcroot_t *wcroot,
10163 const char *local_relpath,
10164 const char *propname,
10165 apr_pool_t *result_pool,
10166 apr_pool_t *scratch_pool)
10169 apr_array_header_t *cached_iprops = NULL;
10170 apr_array_header_t *iprops;
10171 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
10172 svn_sqlite__stmt_t *stmt;
10173 const char *relpath;
10174 const char *expected_parent_repos_relpath = NULL;
10175 const char *parent_relpath;
10177 iprops = apr_array_make(result_pool, 1,
10178 sizeof(svn_prop_inherited_item_t *));
10179 *inherited_props = iprops;
10182 *actual_props = NULL;
10184 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10185 STMT_SELECT_NODE_INFO));
10187 relpath = local_relpath;
10189 /* Walk up to the root of the WC looking for inherited properties. When we
10190 reach the WC root also check for cached inherited properties. */
10191 for (relpath = local_relpath; relpath; relpath = parent_relpath)
10193 svn_boolean_t have_row;
10195 svn_wc__db_status_t status;
10196 apr_hash_t *node_props;
10198 parent_relpath = relpath[0] ? svn_relpath_dirname(relpath, scratch_pool)
10201 svn_pool_clear(iterpool);
10203 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, relpath));
10205 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10208 return svn_error_createf(
10209 SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
10210 _("The node '%s' was not found."),
10211 path_for_error_message(wcroot, relpath,
10214 op_depth = svn_sqlite__column_int(stmt, 0);
10216 status = svn_sqlite__column_token(stmt, 3, presence_map);
10218 if (status != svn_wc__db_status_normal
10219 && status != svn_wc__db_status_incomplete)
10220 return svn_error_createf(
10221 SVN_ERR_WC_PATH_UNEXPECTED_STATUS, svn_sqlite__reset(stmt),
10222 _("The node '%s' has a status that has no properties."),
10223 path_for_error_message(wcroot, relpath,
10228 /* WORKING node. Nothing to check */
10230 else if (expected_parent_repos_relpath)
10232 const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10234 if (strcmp(expected_parent_repos_relpath, repos_relpath) != 0)
10236 /* The child of this node has a different parent than this node
10237 (It is "switched"), so we can stop here. Note that switched
10238 with the same parent is not interesting for us here. */
10239 SVN_ERR(svn_sqlite__reset(stmt));
10243 expected_parent_repos_relpath =
10244 svn_relpath_dirname(expected_parent_repos_relpath, scratch_pool);
10248 const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10250 expected_parent_repos_relpath =
10251 svn_relpath_dirname(repos_relpath, scratch_pool);
10255 && !svn_sqlite__column_is_null(stmt, 16))
10257 /* The node contains a cache. No reason to look further */
10258 SVN_ERR(svn_sqlite__column_iprops(&cached_iprops, stmt, 16,
10259 result_pool, iterpool));
10261 parent_relpath = NULL; /* Stop after this */
10264 SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 14,
10265 iterpool, iterpool));
10267 SVN_ERR(svn_sqlite__reset(stmt));
10269 /* If PARENT_ABSPATH is a parent of LOCAL_ABSPATH, then LOCAL_ABSPATH
10270 can inherit properties from it. */
10271 if (relpath != local_relpath)
10273 apr_hash_t *changed_props;
10275 SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10276 result_pool, iterpool));
10279 node_props = changed_props;
10280 else if (node_props)
10281 node_props = svn_prop_hash_dup(node_props, result_pool);
10283 if (node_props && apr_hash_count(node_props))
10285 /* If we only want PROPNAME filter out any other properties. */
10287 filter_unwanted_props(node_props, propname, iterpool);
10289 if (apr_hash_count(node_props))
10291 svn_prop_inherited_item_t *iprop_elt =
10292 apr_pcalloc(result_pool,
10293 sizeof(svn_prop_inherited_item_t));
10294 iprop_elt->path_or_url = svn_dirent_join(wcroot->abspath,
10298 iprop_elt->prop_hash = node_props;
10299 /* Build the output array in depth-first order. */
10300 svn_sort__array_insert(&iprop_elt, iprops, 0);
10304 else if (actual_props)
10306 apr_hash_t *changed_props;
10308 SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10309 result_pool, iterpool));
10312 *actual_props = changed_props;
10313 else if (node_props)
10314 *actual_props = svn_prop_hash_dup(node_props, result_pool);
10320 for (i = cached_iprops->nelts - 1; i >= 0; i--)
10322 svn_prop_inherited_item_t *cached_iprop =
10323 APR_ARRAY_IDX(cached_iprops, i, svn_prop_inherited_item_t *);
10325 /* An empty property hash in the iprops cache means there are no
10326 inherited properties. */
10327 if (apr_hash_count(cached_iprop->prop_hash) == 0)
10331 filter_unwanted_props(cached_iprop->prop_hash, propname,
10334 /* If we didn't filter everything then keep this iprop. */
10335 if (apr_hash_count(cached_iprop->prop_hash))
10336 svn_sort__array_insert(&cached_iprop, iprops, 0);
10340 if (actual_props && !*actual_props)
10341 *actual_props = apr_hash_make(result_pool);
10343 svn_pool_destroy(iterpool);
10344 return SVN_NO_ERROR;
10348 svn_wc__db_read_inherited_props(apr_array_header_t **iprops,
10349 apr_hash_t **actual_props,
10351 const char *local_abspath,
10352 const char *propname,
10353 apr_pool_t *result_pool,
10354 apr_pool_t *scratch_pool)
10356 svn_wc__db_wcroot_t *wcroot;
10357 const char *local_relpath;
10359 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10361 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10363 scratch_pool, scratch_pool));
10364 VERIFY_USABLE_WCROOT(wcroot);
10366 SVN_WC__DB_WITH_TXN(db_read_inherited_props(iprops, actual_props,
10367 wcroot, local_relpath, propname,
10368 result_pool, scratch_pool),
10371 return SVN_NO_ERROR;
10374 /* The body of svn_wc__db_get_children_with_cached_iprops().
10376 static svn_error_t *
10377 get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10378 svn_wc__db_wcroot_t *wcroot,
10379 const char *local_relpath,
10381 apr_pool_t *result_pool,
10382 apr_pool_t *scratch_pool)
10384 svn_sqlite__stmt_t *stmt;
10385 svn_boolean_t have_row;
10387 *iprop_paths = apr_hash_make(result_pool);
10389 /* First check if LOCAL_RELPATH itself has iprops */
10390 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10391 STMT_SELECT_IPROPS_NODE));
10392 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10393 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10397 const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10399 const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10400 relpath_with_cache,
10402 svn_hash_sets(*iprop_paths, abspath_with_cache,
10403 svn_sqlite__column_text(stmt, 1, result_pool));
10405 SVN_ERR(svn_sqlite__reset(stmt));
10407 if (depth == svn_depth_empty)
10408 return SVN_NO_ERROR;
10410 /* Now fetch information for children or all descendants */
10411 if (depth == svn_depth_files
10412 || depth == svn_depth_immediates)
10414 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10415 STMT_SELECT_IPROPS_CHILDREN));
10417 else /* Default to svn_depth_infinity. */
10419 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10420 STMT_SELECT_IPROPS_RECURSIVE));
10423 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10424 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10428 const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10430 const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10431 relpath_with_cache,
10433 svn_hash_sets(*iprop_paths, abspath_with_cache,
10434 svn_sqlite__column_text(stmt, 1, result_pool));
10435 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10438 SVN_ERR(svn_sqlite__reset(stmt));
10440 /* For depth files we should filter non files */
10441 if (depth == svn_depth_files)
10443 apr_hash_index_t *hi;
10444 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
10446 for (hi = apr_hash_first(scratch_pool, *iprop_paths);
10448 hi = apr_hash_next(hi))
10450 const char *child_abspath = svn__apr_hash_index_key(hi);
10451 const char *child_relpath;
10452 svn_node_kind_t child_kind;
10454 svn_pool_clear(iterpool);
10456 child_relpath = svn_dirent_is_child(local_relpath, child_abspath,
10459 if (! child_relpath)
10461 continue; /* local_relpath itself */
10464 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &child_kind, NULL,
10465 NULL, NULL, NULL, NULL,
10466 NULL, NULL, NULL, NULL,
10467 NULL, NULL, NULL, NULL,
10468 wcroot, child_relpath,
10472 /* Filter if not a file */
10473 if (child_kind != svn_node_file)
10475 svn_hash_sets(*iprop_paths, child_abspath, NULL);
10479 svn_pool_destroy(iterpool);
10482 return SVN_NO_ERROR;
10486 svn_wc__db_get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10488 const char *local_abspath,
10490 apr_pool_t *result_pool,
10491 apr_pool_t *scratch_pool)
10493 svn_wc__db_wcroot_t *wcroot;
10494 const char *local_relpath;
10496 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10498 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10499 local_abspath, scratch_pool,
10501 VERIFY_USABLE_WCROOT(wcroot);
10503 SVN_WC__DB_WITH_TXN(
10504 get_children_with_cached_iprops(iprop_paths, wcroot, local_relpath,
10505 depth, result_pool, scratch_pool),
10508 return SVN_NO_ERROR;
10512 svn_wc__db_read_children_of_working_node(const apr_array_header_t **children,
10514 const char *local_abspath,
10515 apr_pool_t *result_pool,
10516 apr_pool_t *scratch_pool)
10518 svn_wc__db_wcroot_t *wcroot;
10519 const char *local_relpath;
10521 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10523 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10525 scratch_pool, scratch_pool));
10526 VERIFY_USABLE_WCROOT(wcroot);
10528 return gather_children2(children, wcroot, local_relpath,
10529 result_pool, scratch_pool);
10532 /* Helper for svn_wc__db_node_check_replace().
10534 static svn_error_t *
10535 check_replace_txn(svn_boolean_t *is_replace_root_p,
10536 svn_boolean_t *base_replace_p,
10537 svn_boolean_t *is_replace_p,
10538 svn_wc__db_wcroot_t *wcroot,
10539 const char *local_relpath,
10540 apr_pool_t *scratch_pool)
10542 svn_sqlite__stmt_t *stmt;
10543 svn_boolean_t have_row;
10544 svn_boolean_t is_replace = FALSE;
10545 int replaced_op_depth;
10546 svn_wc__db_status_t replaced_status;
10548 /* Our caller initialized the output values to FALSE */
10550 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10551 STMT_SELECT_NODE_INFO));
10553 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10555 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10558 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10559 svn_sqlite__reset(stmt),
10560 _("The node '%s' was not found."),
10561 path_for_error_message(wcroot, local_relpath,
10565 svn_wc__db_status_t status;
10567 status = svn_sqlite__column_token(stmt, 3, presence_map);
10569 if (status != svn_wc__db_status_normal)
10570 return svn_error_trace(svn_sqlite__reset(stmt));
10573 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10576 return svn_error_trace(svn_sqlite__reset(stmt));
10578 replaced_status = svn_sqlite__column_token(stmt, 3, presence_map);
10580 /* If the layer below the add describes a not present or a deleted node,
10581 this is not a replacement. Deleted can only occur if an ancestor is
10582 the delete root. */
10583 if (replaced_status != svn_wc__db_status_not_present
10584 && replaced_status != svn_wc__db_status_excluded
10585 && replaced_status != svn_wc__db_status_server_excluded
10586 && replaced_status != svn_wc__db_status_base_deleted)
10590 *is_replace_p = TRUE;
10593 replaced_op_depth = svn_sqlite__column_int(stmt, 0);
10595 if (base_replace_p)
10597 int op_depth = svn_sqlite__column_int(stmt, 0);
10599 while (op_depth != 0 && have_row)
10601 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10604 op_depth = svn_sqlite__column_int(stmt, 0);
10607 if (have_row && op_depth == 0)
10609 svn_wc__db_status_t base_status;
10611 base_status = svn_sqlite__column_token(stmt, 3, presence_map);
10613 *base_replace_p = (base_status != svn_wc__db_status_not_present);
10617 SVN_ERR(svn_sqlite__reset(stmt));
10619 if (!is_replace_root_p || !is_replace)
10620 return SVN_NO_ERROR;
10622 if (replaced_status != svn_wc__db_status_base_deleted)
10624 int parent_op_depth;
10626 /* Check the current op-depth of the parent to see if we are a replacement
10628 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
10629 svn_relpath_dirname(local_relpath,
10632 SVN_ERR(svn_sqlite__step_row(stmt)); /* Parent must exist as 'normal' */
10634 parent_op_depth = svn_sqlite__column_int(stmt, 0);
10636 if (parent_op_depth >= replaced_op_depth)
10638 /* Did we replace inside our directory? */
10640 *is_replace_root_p = (parent_op_depth == replaced_op_depth);
10641 SVN_ERR(svn_sqlite__reset(stmt));
10642 return SVN_NO_ERROR;
10645 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10648 parent_op_depth = svn_sqlite__column_int(stmt, 0);
10650 SVN_ERR(svn_sqlite__reset(stmt));
10653 *is_replace_root_p = TRUE; /* Parent is no replacement */
10654 else if (parent_op_depth < replaced_op_depth)
10655 *is_replace_root_p = TRUE; /* Parent replaces a lower layer */
10656 /*else // No replacement root */
10659 return SVN_NO_ERROR;
10663 svn_wc__db_node_check_replace(svn_boolean_t *is_replace_root,
10664 svn_boolean_t *base_replace,
10665 svn_boolean_t *is_replace,
10667 const char *local_abspath,
10668 apr_pool_t *scratch_pool)
10670 svn_wc__db_wcroot_t *wcroot;
10671 const char *local_relpath;
10673 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10675 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10677 scratch_pool, scratch_pool));
10678 VERIFY_USABLE_WCROOT(wcroot);
10680 if (is_replace_root)
10681 *is_replace_root = FALSE;
10683 *base_replace = FALSE;
10685 *is_replace = FALSE;
10687 if (local_relpath[0] == '\0')
10688 return SVN_NO_ERROR; /* Working copy root can't be replaced */
10690 SVN_WC__DB_WITH_TXN(
10691 check_replace_txn(is_replace_root, base_replace, is_replace,
10692 wcroot, local_relpath, scratch_pool),
10695 return SVN_NO_ERROR;
10699 svn_wc__db_read_children(const apr_array_header_t **children,
10701 const char *local_abspath,
10702 apr_pool_t *result_pool,
10703 apr_pool_t *scratch_pool)
10705 svn_wc__db_wcroot_t *wcroot;
10706 const char *local_relpath;
10708 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10710 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10712 scratch_pool, scratch_pool));
10713 VERIFY_USABLE_WCROOT(wcroot);
10715 return gather_children(children, wcroot, local_relpath,
10716 result_pool, scratch_pool);
10721 static svn_error_t *
10722 relocate_txn(svn_wc__db_wcroot_t *wcroot,
10723 const char *local_relpath,
10724 const char *repos_root_url,
10725 const char *repos_uuid,
10726 svn_boolean_t have_base_node,
10727 apr_int64_t old_repos_id,
10728 apr_pool_t *scratch_pool)
10730 svn_sqlite__stmt_t *stmt;
10731 apr_int64_t new_repos_id;
10733 /* This function affects all the children of the given local_relpath,
10734 but the way that it does this is through the repos inheritance mechanism.
10735 So, we only need to rewrite the repos_id of the given local_relpath,
10736 as well as any children with a non-null repos_id, as well as various
10737 repos_id fields in the locks and working_node tables.
10740 /* Get the repos_id for the new repository. */
10741 SVN_ERR(create_repos_id(&new_repos_id, repos_root_url, repos_uuid,
10742 wcroot->sdb, scratch_pool));
10744 /* Set the (base and working) repos_ids and clear the dav_caches */
10745 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10746 STMT_RECURSIVE_UPDATE_NODE_REPO));
10747 SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
10748 old_repos_id, new_repos_id));
10749 SVN_ERR(svn_sqlite__step_done(stmt));
10751 if (have_base_node)
10753 /* Update any locks for the root or its children. */
10754 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10755 STMT_UPDATE_LOCK_REPOS_ID));
10756 SVN_ERR(svn_sqlite__bindf(stmt, "ii", old_repos_id, new_repos_id));
10757 SVN_ERR(svn_sqlite__step_done(stmt));
10760 return SVN_NO_ERROR;
10765 svn_wc__db_global_relocate(svn_wc__db_t *db,
10766 const char *local_dir_abspath,
10767 const char *repos_root_url,
10768 apr_pool_t *scratch_pool)
10770 svn_wc__db_wcroot_t *wcroot;
10771 const char *local_relpath;
10772 const char *local_dir_relpath;
10773 svn_wc__db_status_t status;
10774 const char *repos_uuid;
10775 svn_boolean_t have_base_node;
10776 apr_int64_t old_repos_id;
10778 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
10779 /* ### assert that we were passed a directory? */
10781 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_dir_relpath,
10782 db, local_dir_abspath, scratch_pool, scratch_pool));
10783 VERIFY_USABLE_WCROOT(wcroot);
10784 local_relpath = local_dir_relpath;
10786 SVN_ERR(read_info(&status,
10787 NULL, NULL, NULL, &old_repos_id,
10788 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10789 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10791 &have_base_node, NULL, NULL,
10792 wcroot, local_relpath,
10793 scratch_pool, scratch_pool));
10795 if (status == svn_wc__db_status_excluded)
10797 /* The parent cannot be excluded, so look at the parent and then
10798 adjust the relpath */
10799 const char *parent_relpath = svn_relpath_dirname(local_dir_relpath,
10801 SVN_ERR(read_info(&status,
10802 NULL, NULL, NULL, &old_repos_id,
10803 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10804 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10807 wcroot, parent_relpath,
10808 scratch_pool, scratch_pool));
10809 local_dir_relpath = parent_relpath;
10812 if (old_repos_id == INVALID_REPOS_ID)
10814 /* Do we need to support relocating something that is
10815 added/deleted/excluded without relocating the parent? If not
10816 then perhaps relpath, root_url and uuid should be passed down
10817 to the children so that they don't have to scan? */
10819 if (status == svn_wc__db_status_deleted)
10821 const char *work_del_relpath;
10823 SVN_ERR(scan_deletion_txn(NULL, NULL,
10824 &work_del_relpath, NULL,
10825 wcroot, local_dir_relpath,
10828 if (work_del_relpath)
10830 /* Deleted within a copy/move */
10832 /* The parent of the delete is added. */
10833 status = svn_wc__db_status_added;
10834 local_dir_relpath = svn_relpath_dirname(work_del_relpath,
10839 if (status == svn_wc__db_status_added)
10841 SVN_ERR(scan_addition(NULL, NULL, NULL, &old_repos_id,
10842 NULL, NULL, NULL, NULL, NULL, NULL,
10843 wcroot, local_dir_relpath,
10844 scratch_pool, scratch_pool));
10847 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL,
10849 NULL, NULL, NULL, NULL, NULL,
10850 NULL, NULL, NULL, NULL, NULL,
10851 wcroot, local_dir_relpath,
10852 scratch_pool, scratch_pool));
10855 SVN_ERR(svn_wc__db_fetch_repos_info(NULL, &repos_uuid, wcroot->sdb,
10856 old_repos_id, scratch_pool));
10857 SVN_ERR_ASSERT(repos_uuid);
10859 SVN_WC__DB_WITH_TXN(
10860 relocate_txn(wcroot, local_relpath, repos_root_url, repos_uuid,
10861 have_base_node, old_repos_id, scratch_pool),
10864 return SVN_NO_ERROR;
10868 /* Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of
10869 (WCROOT, LOCAL_RELPATH), directly if its BASE row exists or implied from
10870 its parent's BASE row if not. In the latter case, error if the parent
10871 BASE row does not exist. */
10872 static svn_error_t *
10873 determine_repos_info(apr_int64_t *repos_id,
10874 const char **repos_relpath,
10875 svn_wc__db_wcroot_t *wcroot,
10876 const char *local_relpath,
10877 apr_pool_t *result_pool,
10878 apr_pool_t *scratch_pool)
10880 svn_sqlite__stmt_t *stmt;
10881 svn_boolean_t have_row;
10882 const char *repos_parent_relpath;
10883 const char *local_parent_relpath, *name;
10885 /* ### is it faster to fetch fewer columns? */
10887 /* Prefer the current node's repository information. */
10888 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10889 STMT_SELECT_BASE_NODE));
10890 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10891 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10895 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 0));
10896 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1));
10898 *repos_id = svn_sqlite__column_int64(stmt, 0);
10899 *repos_relpath = svn_sqlite__column_text(stmt, 1, result_pool);
10901 return svn_error_trace(svn_sqlite__reset(stmt));
10904 SVN_ERR(svn_sqlite__reset(stmt));
10906 /* This was a child node within this wcroot. We want to look at the
10907 BASE node of the directory. */
10908 svn_relpath_split(&local_parent_relpath, &name, local_relpath, scratch_pool);
10910 /* The REPOS_ID will be the same (### until we support mixed-repos) */
10911 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
10912 &repos_parent_relpath, repos_id,
10913 NULL, NULL, NULL, NULL, NULL,
10914 NULL, NULL, NULL, NULL, NULL,
10915 wcroot, local_parent_relpath,
10916 scratch_pool, scratch_pool));
10918 *repos_relpath = svn_relpath_join(repos_parent_relpath, name, result_pool);
10920 return SVN_NO_ERROR;
10923 /* Helper for svn_wc__db_global_commit()
10925 Makes local_relpath and all its descendants at the same op-depth represent
10926 the copy origin repos_id:repos_relpath@revision.
10928 This code is only valid to fix-up a move from an old location, to a new
10929 location during a commit.
10932 * local_relpath is not the working copy root (can't be moved)
10933 * repos_relpath is not the repository root (can't be moved)
10935 static svn_error_t *
10936 moved_descendant_commit(svn_wc__db_wcroot_t *wcroot,
10937 const char *local_relpath,
10939 apr_int64_t repos_id,
10940 const char *repos_relpath,
10941 svn_revnum_t revision,
10942 apr_pool_t *scratch_pool)
10944 apr_hash_t *children;
10945 apr_pool_t *iterpool;
10946 svn_sqlite__stmt_t *stmt;
10947 svn_boolean_t have_row;
10948 apr_hash_index_t *hi;
10950 SVN_ERR_ASSERT(*local_relpath != '\0'
10951 && *repos_relpath != '\0');
10953 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10954 STMT_SELECT_MOVED_DESCENDANTS));
10955 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
10959 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10961 return svn_error_trace(svn_sqlite__reset(stmt));
10963 children = apr_hash_make(scratch_pool);
10965 /* First, obtain all moved children */
10966 /* To keep error handling simple, first cache them in a hashtable */
10969 const char *src_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
10970 const char *to_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
10972 svn_hash_sets(children, src_relpath, to_relpath);
10974 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10976 SVN_ERR(svn_sqlite__reset(stmt));
10978 /* Then update them */
10979 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10980 STMT_COMMIT_UPDATE_ORIGIN));
10982 iterpool = svn_pool_create(scratch_pool);
10983 for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
10985 const char *src_relpath = svn__apr_hash_index_key(hi);
10986 const char *to_relpath = svn__apr_hash_index_val(hi);
10987 const char *new_repos_relpath;
10988 int to_op_depth = relpath_depth(to_relpath);
10991 svn_pool_clear(iterpool);
10993 SVN_ERR_ASSERT(to_op_depth > 0);
10995 new_repos_relpath = svn_relpath_join(
10997 svn_relpath_skip_ancestor(local_relpath,
11001 SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11007 SVN_ERR(svn_sqlite__update(&affected, stmt));
11010 /* Enable in release code?
11011 Broken moves are not fatal yet, but this assertion would break
11013 SVN_ERR_ASSERT(affected >= 1); /* If this fails there is no move dest */
11016 SVN_ERR(moved_descendant_commit(wcroot, to_relpath, to_op_depth,
11017 repos_id, new_repos_relpath, revision,
11021 svn_pool_destroy(iterpool);
11022 return SVN_NO_ERROR;
11025 /* Helper for svn_wc__db_global_commit()
11027 Moves all nodes below LOCAL_RELPATH from op-depth OP_DEPTH to op-depth 0
11028 (BASE), setting their presence to 'not-present' if their presence wasn't
11031 Makes all nodes below LOCAL_RELPATH represent the descendants of repository
11032 location repos_id:repos_relpath@revision.
11035 * local_relpath is not the working copy root (can't be replaced)
11036 * repos_relpath is not the repository root (can't be replaced)
11038 static svn_error_t *
11039 descendant_commit(svn_wc__db_wcroot_t *wcroot,
11040 const char *local_relpath,
11042 apr_int64_t repos_id,
11043 const char *repos_relpath,
11044 svn_revnum_t revision,
11045 apr_pool_t *scratch_pool)
11047 svn_sqlite__stmt_t *stmt;
11049 SVN_ERR_ASSERT(*local_relpath != '\0'
11050 && *repos_relpath != '\0');
11052 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11053 STMT_COMMIT_DESCENDANTS_TO_BASE));
11055 SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11062 SVN_ERR(svn_sqlite__update(NULL, stmt));
11064 return SVN_NO_ERROR;
11067 /* The body of svn_wc__db_global_commit().
11069 static svn_error_t *
11070 commit_node(svn_wc__db_wcroot_t *wcroot,
11071 const char *local_relpath,
11072 svn_revnum_t new_revision,
11073 svn_revnum_t changed_rev,
11074 apr_time_t changed_date,
11075 const char *changed_author,
11076 const svn_checksum_t *new_checksum,
11077 const apr_array_header_t *new_children,
11078 apr_hash_t *new_dav_cache,
11079 svn_boolean_t keep_changelist,
11080 svn_boolean_t no_unlock,
11081 const svn_skel_t *work_items,
11082 apr_pool_t *scratch_pool)
11084 svn_sqlite__stmt_t *stmt_info;
11085 svn_sqlite__stmt_t *stmt_act;
11086 svn_boolean_t have_act;
11087 svn_string_t prop_blob = { 0 };
11088 svn_string_t inherited_prop_blob = { 0 };
11089 const char *changelist = NULL;
11090 const char *parent_relpath;
11091 svn_wc__db_status_t new_presence;
11092 svn_node_kind_t new_kind;
11093 const char *new_depth_str = NULL;
11094 svn_sqlite__stmt_t *stmt;
11095 apr_int64_t repos_id;
11096 const char *repos_relpath;
11098 svn_wc__db_status_t old_presence;
11100 /* If we are adding a file or directory, then we need to get
11101 repository information from the parent node since "this node" does
11104 For existing nodes, we should retain the (potentially-switched)
11105 repository information. */
11106 SVN_ERR(determine_repos_info(&repos_id, &repos_relpath,
11107 wcroot, local_relpath,
11108 scratch_pool, scratch_pool));
11110 /* ### is it better to select only the data needed? */
11111 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
11112 STMT_SELECT_NODE_INFO));
11113 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
11114 SVN_ERR(svn_sqlite__step_row(stmt_info));
11116 SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
11117 STMT_SELECT_ACTUAL_NODE));
11118 SVN_ERR(svn_sqlite__bindf(stmt_act, "is",
11119 wcroot->wc_id, local_relpath));
11120 SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
11122 /* There should be something to commit! */
11124 op_depth = svn_sqlite__column_int(stmt_info, 0);
11126 /* Figure out the new node's kind. It will be whatever is in WORKING_NODE,
11127 or there will be a BASE_NODE that has it. */
11128 new_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
11130 /* What will the new depth be? */
11131 if (new_kind == svn_node_dir)
11132 new_depth_str = svn_sqlite__column_text(stmt_info, 11, scratch_pool);
11134 /* Check that the repository information is not being changed. */
11137 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 1));
11138 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 2));
11140 /* A commit cannot change these values. */
11141 SVN_ERR_ASSERT(repos_id == svn_sqlite__column_int64(stmt_info, 1));
11142 SVN_ERR_ASSERT(strcmp(repos_relpath,
11143 svn_sqlite__column_text(stmt_info, 2, NULL)) == 0);
11146 /* Find the appropriate new properties -- ACTUAL overrides any properties
11147 in WORKING that arrived as part of a copy/move.
11149 Note: we'll keep them as a big blob of data, rather than
11150 deserialize/serialize them. */
11152 prop_blob.data = svn_sqlite__column_blob(stmt_act, 1, &prop_blob.len,
11154 if (prop_blob.data == NULL)
11155 prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len,
11158 inherited_prop_blob.data = svn_sqlite__column_blob(stmt_info, 16,
11159 &inherited_prop_blob.len,
11162 if (keep_changelist && have_act)
11163 changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool);
11165 old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map);
11167 /* ### other stuff? */
11169 SVN_ERR(svn_sqlite__reset(stmt_info));
11170 SVN_ERR(svn_sqlite__reset(stmt_act));
11176 /* This removes all layers of this node and at the same time determines
11177 if we need to remove shadowed layers below our descendants. */
11179 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11180 STMT_DELETE_NODE_ALL_LAYERS));
11181 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11182 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
11184 if (affected_rows > 1)
11186 /* We commit a shadowing operation
11188 1) Remove all shadowed nodes
11189 2) And remove all nodes that have a base-deleted as lowest layer,
11190 because 1) removed that layer */
11192 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11193 STMT_DELETE_SHADOWED_RECURSIVE));
11195 SVN_ERR(svn_sqlite__bindf(stmt,
11201 SVN_ERR(svn_sqlite__step_done(stmt));
11204 /* Note that while these two calls look so similar that they might
11205 be integrated, they really affect a different op-depth and
11206 completely different nodes (via a different recursion pattern). */
11208 /* Collapse descendants of the current op_depth in layer 0 */
11209 SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth,
11210 repos_id, repos_relpath, new_revision,
11213 /* And make the recorded local moves represent moves of the node we just
11215 SVN_ERR(moved_descendant_commit(wcroot, local_relpath, 0,
11216 repos_id, repos_relpath, new_revision,
11219 /* This node is no longer modified, so no node was moved here */
11220 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11221 STMT_CLEAR_MOVED_TO_FROM_DEST));
11222 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
11225 SVN_ERR(svn_sqlite__step_done(stmt));
11228 /* Update or add the BASE_NODE row with all the new information. */
11230 if (*local_relpath == '\0')
11231 parent_relpath = NULL;
11233 parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
11235 /* Preserve any incomplete status */
11236 new_presence = (old_presence == svn_wc__db_status_incomplete
11237 ? svn_wc__db_status_incomplete
11238 : svn_wc__db_status_normal);
11240 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11241 STMT_APPLY_CHANGES_TO_BASE_NODE));
11242 /* symlink_target not yet used */
11243 SVN_ERR(svn_sqlite__bindf(stmt, "issisrtstrisnbn",
11244 wcroot->wc_id, local_relpath,
11249 presence_map, new_presence,
11251 kind_map, new_kind,
11255 prop_blob.data, prop_blob.len));
11257 SVN_ERR(svn_sqlite__bind_checksum(stmt, 13, new_checksum,
11259 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, new_dav_cache,
11261 if (inherited_prop_blob.data != NULL)
11263 SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data,
11264 inherited_prop_blob.len));
11267 SVN_ERR(svn_sqlite__step_done(stmt));
11271 if (keep_changelist && changelist != NULL)
11273 /* The user told us to keep the changelist. Replace the row in
11274 ACTUAL_NODE with the basic keys and the changelist. */
11275 SVN_ERR(svn_sqlite__get_statement(
11276 &stmt, wcroot->sdb,
11277 STMT_RESET_ACTUAL_WITH_CHANGELIST));
11278 SVN_ERR(svn_sqlite__bindf(stmt, "isss",
11279 wcroot->wc_id, local_relpath,
11280 svn_relpath_dirname(local_relpath,
11283 SVN_ERR(svn_sqlite__step_done(stmt));
11287 /* Toss the ACTUAL_NODE row. */
11288 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11289 STMT_DELETE_ACTUAL_NODE));
11290 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11291 SVN_ERR(svn_sqlite__step_done(stmt));
11295 if (new_kind == svn_node_dir)
11297 /* When committing a directory, we should have its new children. */
11298 /* ### one day. just not today. */
11300 SVN_ERR_ASSERT(new_children != NULL);
11303 /* ### process the children */
11308 svn_sqlite__stmt_t *lock_stmt;
11310 SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
11311 STMT_DELETE_LOCK_RECURSIVELY));
11312 SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
11313 SVN_ERR(svn_sqlite__step_done(lock_stmt));
11316 /* Install any work items into the queue, as part of this transaction. */
11317 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
11319 return SVN_NO_ERROR;
11324 svn_wc__db_global_commit(svn_wc__db_t *db,
11325 const char *local_abspath,
11326 svn_revnum_t new_revision,
11327 svn_revnum_t changed_revision,
11328 apr_time_t changed_date,
11329 const char *changed_author,
11330 const svn_checksum_t *new_checksum,
11331 const apr_array_header_t *new_children,
11332 apr_hash_t *new_dav_cache,
11333 svn_boolean_t keep_changelist,
11334 svn_boolean_t no_unlock,
11335 const svn_skel_t *work_items,
11336 apr_pool_t *scratch_pool)
11338 const char *local_relpath;
11339 svn_wc__db_wcroot_t *wcroot;
11341 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11342 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
11343 SVN_ERR_ASSERT(new_checksum == NULL || new_children == NULL);
11345 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11346 local_abspath, scratch_pool, scratch_pool));
11347 VERIFY_USABLE_WCROOT(wcroot);
11349 SVN_WC__DB_WITH_TXN(
11350 commit_node(wcroot, local_relpath,
11351 new_revision, changed_revision, changed_date, changed_author,
11352 new_checksum, new_children, new_dav_cache, keep_changelist,
11353 no_unlock, work_items, scratch_pool),
11356 /* We *totally* monkeyed the entries. Toss 'em. */
11357 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
11359 return SVN_NO_ERROR;
11364 svn_wc__db_global_update(svn_wc__db_t *db,
11365 const char *local_abspath,
11366 svn_node_kind_t new_kind,
11367 const char *new_repos_relpath,
11368 svn_revnum_t new_revision,
11369 const apr_hash_t *new_props,
11370 svn_revnum_t new_changed_rev,
11371 apr_time_t new_changed_date,
11372 const char *new_changed_author,
11373 const apr_array_header_t *new_children,
11374 const svn_checksum_t *new_checksum,
11375 const char *new_target,
11376 const apr_hash_t *new_dav_cache,
11377 const svn_skel_t *conflict,
11378 const svn_skel_t *work_items,
11379 apr_pool_t *scratch_pool)
11384 svn_wc__db_wcroot_t *wcroot;
11385 const char *local_relpath;
11387 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11388 /* ### allow NULL for NEW_REPOS_RELPATH to indicate "no change"? */
11389 SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
11390 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
11391 SVN_ERR_ASSERT(new_props != NULL);
11392 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_changed_rev));
11393 SVN_ERR_ASSERT((new_children != NULL
11394 && new_checksum == NULL
11395 && new_target == NULL)
11396 || (new_children == NULL
11397 && new_checksum != NULL
11398 && new_target == NULL)
11399 || (new_children == NULL
11400 && new_checksum == NULL
11401 && new_target != NULL));
11403 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11404 local_abspath, scratch_pool, scratch_pool));
11405 VERIFY_USABLE_WCROOT(wcroot);
11407 SVN_WC__DB_WITH_TXN(
11408 update_node(wcroot, local_relpath,
11409 new_repos_relpath, new_revision, new_props,
11410 new_changed_rev, new_changed_date, new_changed_author,
11411 new_children, new_checksum, new_target,
11412 conflict, work_items, scratch_pool),
11415 /* We *totally* monkeyed the entries. Toss 'em. */
11416 SVN_ERR(flush_entries(wcroot, local_abspath, scratch_pool));
11418 return SVN_NO_ERROR;
11422 /* Sets a base nodes revision, repository relative path, and/or inherited
11423 propertis. If LOCAL_ABSPATH's rev (REV) is valid, set its revision. If
11424 SET_REPOS_RELPATH is TRUE set its repository relative path to REPOS_RELPATH
11425 (and make sure its REPOS_ID is still valid). If IPROPS is not NULL set its
11426 inherited properties to IPROPS, if IPROPS is NULL then clear any the iprops
11427 cache for the base node.
11429 static svn_error_t *
11430 db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot,
11431 const char *local_relpath,
11432 apr_array_header_t *iprops,
11434 svn_boolean_t set_repos_relpath,
11435 const char *repos_relpath,
11436 apr_int64_t repos_id,
11437 apr_pool_t *scratch_pool)
11439 svn_sqlite__stmt_t *stmt;
11441 SVN_ERR(flush_entries(wcroot,
11442 svn_dirent_join(wcroot->abspath, local_relpath,
11444 svn_depth_empty, scratch_pool));
11447 if (SVN_IS_VALID_REVNUM(rev))
11449 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11450 STMT_UPDATE_BASE_REVISION));
11452 SVN_ERR(svn_sqlite__bindf(stmt, "isr", wcroot->wc_id, local_relpath,
11455 SVN_ERR(svn_sqlite__step_done(stmt));
11458 if (set_repos_relpath)
11460 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11461 STMT_UPDATE_BASE_REPOS));
11463 SVN_ERR(svn_sqlite__bindf(stmt, "isis", wcroot->wc_id, local_relpath,
11464 repos_id, repos_relpath));
11466 SVN_ERR(svn_sqlite__step_done(stmt));
11469 /* Set or clear iprops. */
11470 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11471 STMT_UPDATE_IPROP));
11472 SVN_ERR(svn_sqlite__bindf(stmt, "is",
11475 SVN_ERR(svn_sqlite__bind_iprops(stmt, 3, iprops, scratch_pool));
11476 SVN_ERR(svn_sqlite__step_done(stmt));
11478 return SVN_NO_ERROR;
11481 /* The main body of bump_revisions_post_update().
11483 * Tweak the information for LOCAL_RELPATH in WCROOT. If NEW_REPOS_RELPATH is
11484 * non-NULL update the entry to the new url specified by NEW_REPOS_RELPATH,
11485 * NEW_REPOS_ID. If NEW_REV is valid, make this the node's working revision.
11487 * If WCROOT_IPROPS is not NULL it is a hash mapping const char * absolute
11488 * working copy paths to depth-first ordered arrays of
11489 * svn_prop_inherited_item_t * structures. If the absolute path equivalent
11490 * of LOCAL_RELPATH exists in WCROOT_IPROPS, then set the hashed value as the
11491 * node's inherited properties.
11493 * Unless S_ROOT is TRUE the tweaks might cause the node for LOCAL_ABSPATH to
11494 * be removed from the WC; if IS_ROOT is TRUE this will not happen.
11496 static svn_error_t *
11497 bump_node_revision(svn_wc__db_wcroot_t *wcroot,
11498 const char *local_relpath,
11499 apr_int64_t new_repos_id,
11500 const char *new_repos_relpath,
11501 svn_revnum_t new_rev,
11503 apr_hash_t *exclude_relpaths,
11504 apr_hash_t *wcroot_iprops,
11505 svn_boolean_t is_root,
11506 svn_boolean_t skip_when_dir,
11508 apr_pool_t *scratch_pool)
11510 apr_pool_t *iterpool;
11511 const apr_array_header_t *children;
11513 svn_wc__db_status_t status;
11514 svn_node_kind_t db_kind;
11515 svn_revnum_t revision;
11516 const char *repos_relpath;
11517 apr_int64_t repos_id;
11518 svn_boolean_t set_repos_relpath = FALSE;
11519 svn_boolean_t update_root;
11520 svn_depth_t depth_below_here = depth;
11521 apr_array_header_t *iprops = NULL;
11523 /* Skip an excluded path and its descendants. */
11524 if (svn_hash_gets(exclude_relpaths, local_relpath))
11525 return SVN_NO_ERROR;
11527 SVN_ERR(svn_wc__db_base_get_info_internal(&status, &db_kind, &revision,
11528 &repos_relpath, &repos_id,
11529 NULL, NULL, NULL, NULL, NULL,
11530 NULL, NULL, NULL, NULL, &update_root,
11531 wcroot, local_relpath,
11532 scratch_pool, scratch_pool));
11534 /* Skip file externals */
11536 && db_kind == svn_node_file
11538 return SVN_NO_ERROR;
11540 if (skip_when_dir && db_kind == svn_node_dir)
11541 return SVN_NO_ERROR;
11543 /* If the node is still marked 'not-present', then the server did not
11544 re-add it. So it's really gone in this revision, thus we remove the node.
11546 If the node is still marked 'server-excluded' and yet is not the same
11547 revision as new_rev, then the server did not re-add it, nor
11548 re-server-exclude it, so we can remove the node. */
11550 && (status == svn_wc__db_status_not_present
11551 || (status == svn_wc__db_status_server_excluded &&
11552 revision != new_rev)))
11554 return svn_error_trace(db_base_remove(wcroot, local_relpath,
11555 db, FALSE, FALSE, FALSE,
11556 SVN_INVALID_REVNUM,
11557 NULL, NULL, scratch_pool));
11560 if (new_repos_relpath != NULL && strcmp(repos_relpath, new_repos_relpath))
11561 set_repos_relpath = TRUE;
11564 iprops = svn_hash_gets(wcroot_iprops,
11565 svn_dirent_join(wcroot->abspath, local_relpath,
11569 || set_repos_relpath
11570 || (SVN_IS_VALID_REVNUM(new_rev) && new_rev != revision))
11572 SVN_ERR(db_op_set_rev_repos_relpath_iprops(wcroot, local_relpath,
11581 if (depth <= svn_depth_empty
11582 || db_kind != svn_node_dir
11583 || status == svn_wc__db_status_server_excluded
11584 || status == svn_wc__db_status_excluded
11585 || status == svn_wc__db_status_not_present)
11586 return SVN_NO_ERROR;
11588 /* And now recurse over the children */
11590 depth_below_here = depth;
11592 if (depth == svn_depth_immediates || depth == svn_depth_files)
11593 depth_below_here = svn_depth_empty;
11595 iterpool = svn_pool_create(scratch_pool);
11597 SVN_ERR(gather_repo_children(&children, wcroot, local_relpath, 0,
11598 scratch_pool, iterpool));
11599 for (i = 0; i < children->nelts; i++)
11601 const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
11602 const char *child_local_relpath;
11603 const char *child_repos_relpath = NULL;
11605 svn_pool_clear(iterpool);
11607 /* Derive the new URL for the current (child) entry */
11608 if (new_repos_relpath)
11609 child_repos_relpath = svn_relpath_join(new_repos_relpath,
11610 child_basename, iterpool);
11612 child_local_relpath = svn_relpath_join(local_relpath, child_basename,
11615 SVN_ERR(bump_node_revision(wcroot, child_local_relpath, new_repos_id,
11616 child_repos_relpath, new_rev,
11618 exclude_relpaths, wcroot_iprops,
11619 FALSE /* is_root */,
11620 (depth < svn_depth_immediates), db,
11625 svn_pool_destroy(iterpool);
11627 return SVN_NO_ERROR;
11630 /* Helper for svn_wc__db_op_bump_revisions_post_update().
11632 static svn_error_t *
11633 bump_revisions_post_update(svn_wc__db_wcroot_t *wcroot,
11634 const char *local_relpath,
11637 const char *new_repos_relpath,
11638 const char *new_repos_root_url,
11639 const char *new_repos_uuid,
11640 svn_revnum_t new_revision,
11641 apr_hash_t *exclude_relpaths,
11642 apr_hash_t *wcroot_iprops,
11643 svn_wc_notify_func2_t notify_func,
11644 void *notify_baton,
11645 apr_pool_t *scratch_pool)
11647 svn_wc__db_status_t status;
11648 svn_node_kind_t kind;
11650 apr_int64_t new_repos_id = INVALID_REPOS_ID;
11652 err = svn_wc__db_base_get_info_internal(&status, &kind, NULL, NULL, NULL,
11653 NULL, NULL, NULL, NULL, NULL, NULL,
11654 NULL, NULL, NULL, NULL,
11655 wcroot, local_relpath,
11656 scratch_pool, scratch_pool);
11657 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
11659 svn_error_clear(err);
11660 return SVN_NO_ERROR;
11667 case svn_wc__db_status_excluded:
11668 case svn_wc__db_status_server_excluded:
11669 case svn_wc__db_status_not_present:
11670 return SVN_NO_ERROR;
11672 /* Explicitly ignore other statii */
11677 if (new_repos_root_url != NULL)
11678 SVN_ERR(create_repos_id(&new_repos_id, new_repos_root_url,
11680 wcroot->sdb, scratch_pool));
11682 SVN_ERR(bump_node_revision(wcroot, local_relpath, new_repos_id,
11683 new_repos_relpath, new_revision,
11684 depth, exclude_relpaths,
11686 TRUE /* is_root */, FALSE, db,
11689 SVN_ERR(svn_wc__db_bump_moved_away(wcroot, local_relpath, depth, db,
11692 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, SVN_INVALID_REVNUM,
11693 SVN_INVALID_REVNUM, notify_func,
11694 notify_baton, scratch_pool));
11696 return SVN_NO_ERROR;
11700 svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db,
11701 const char *local_abspath,
11703 const char *new_repos_relpath,
11704 const char *new_repos_root_url,
11705 const char *new_repos_uuid,
11706 svn_revnum_t new_revision,
11707 apr_hash_t *exclude_relpaths,
11708 apr_hash_t *wcroot_iprops,
11709 svn_wc_notify_func2_t notify_func,
11710 void *notify_baton,
11711 apr_pool_t *scratch_pool)
11713 const char *local_relpath;
11714 svn_wc__db_wcroot_t *wcroot;
11716 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11717 local_abspath, scratch_pool, scratch_pool));
11719 VERIFY_USABLE_WCROOT(wcroot);
11721 if (svn_hash_gets(exclude_relpaths, local_relpath))
11722 return SVN_NO_ERROR;
11724 if (depth == svn_depth_unknown)
11725 depth = svn_depth_infinity;
11727 SVN_WC__DB_WITH_TXN(
11728 bump_revisions_post_update(wcroot, local_relpath, db,
11729 depth, new_repos_relpath, new_repos_root_url,
11730 new_repos_uuid, new_revision,
11731 exclude_relpaths, wcroot_iprops,
11732 notify_func, notify_baton, scratch_pool),
11735 return SVN_NO_ERROR;
11738 /* The body of svn_wc__db_lock_add().
11740 static svn_error_t *
11741 lock_add_txn(svn_wc__db_wcroot_t *wcroot,
11742 const char *local_relpath,
11743 const svn_wc__db_lock_t *lock,
11744 apr_pool_t *scratch_pool)
11746 svn_sqlite__stmt_t *stmt;
11747 const char *repos_relpath;
11748 apr_int64_t repos_id;
11750 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11751 &repos_relpath, &repos_id,
11752 NULL, NULL, NULL, NULL, NULL,
11753 NULL, NULL, NULL, NULL, NULL,
11754 wcroot, local_relpath,
11755 scratch_pool, scratch_pool));
11757 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_LOCK));
11758 SVN_ERR(svn_sqlite__bindf(stmt, "iss",
11759 repos_id, repos_relpath, lock->token));
11761 if (lock->owner != NULL)
11762 SVN_ERR(svn_sqlite__bind_text(stmt, 4, lock->owner));
11764 if (lock->comment != NULL)
11765 SVN_ERR(svn_sqlite__bind_text(stmt, 5, lock->comment));
11767 if (lock->date != 0)
11768 SVN_ERR(svn_sqlite__bind_int64(stmt, 6, lock->date));
11770 SVN_ERR(svn_sqlite__insert(NULL, stmt));
11772 return SVN_NO_ERROR;
11777 svn_wc__db_lock_add(svn_wc__db_t *db,
11778 const char *local_abspath,
11779 const svn_wc__db_lock_t *lock,
11780 apr_pool_t *scratch_pool)
11782 svn_wc__db_wcroot_t *wcroot;
11783 const char *local_relpath;
11785 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11786 SVN_ERR_ASSERT(lock != NULL);
11788 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11789 local_abspath, scratch_pool, scratch_pool));
11790 VERIFY_USABLE_WCROOT(wcroot);
11792 SVN_WC__DB_WITH_TXN(
11793 lock_add_txn(wcroot, local_relpath, lock, scratch_pool),
11796 /* There may be some entries, and the lock info is now out of date. */
11797 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
11799 return SVN_NO_ERROR;
11803 /* The body of svn_wc__db_lock_remove().
11805 static svn_error_t *
11806 lock_remove_txn(svn_wc__db_wcroot_t *wcroot,
11807 const char *local_relpath,
11808 apr_pool_t *scratch_pool)
11810 const char *repos_relpath;
11811 apr_int64_t repos_id;
11812 svn_sqlite__stmt_t *stmt;
11814 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11815 &repos_relpath, &repos_id,
11816 NULL, NULL, NULL, NULL, NULL,
11817 NULL, NULL, NULL, NULL, NULL,
11818 wcroot, local_relpath,
11819 scratch_pool, scratch_pool));
11821 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11822 STMT_DELETE_LOCK));
11823 SVN_ERR(svn_sqlite__bindf(stmt, "is", repos_id, repos_relpath));
11825 SVN_ERR(svn_sqlite__step_done(stmt));
11827 return SVN_NO_ERROR;
11832 svn_wc__db_lock_remove(svn_wc__db_t *db,
11833 const char *local_abspath,
11834 apr_pool_t *scratch_pool)
11836 svn_wc__db_wcroot_t *wcroot;
11837 const char *local_relpath;
11839 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11841 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11842 local_abspath, scratch_pool, scratch_pool));
11843 VERIFY_USABLE_WCROOT(wcroot);
11845 SVN_WC__DB_WITH_TXN(
11846 lock_remove_txn(wcroot, local_relpath, scratch_pool),
11849 /* There may be some entries, and the lock info is now out of date. */
11850 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
11852 return SVN_NO_ERROR;
11857 svn_wc__db_scan_base_repos(const char **repos_relpath,
11858 const char **repos_root_url,
11859 const char **repos_uuid,
11861 const char *local_abspath,
11862 apr_pool_t *result_pool,
11863 apr_pool_t *scratch_pool)
11865 svn_wc__db_wcroot_t *wcroot;
11866 const char *local_relpath;
11867 apr_int64_t repos_id;
11869 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11871 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11872 local_abspath, scratch_pool, scratch_pool));
11873 VERIFY_USABLE_WCROOT(wcroot);
11875 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11876 repos_relpath, &repos_id,
11877 NULL, NULL, NULL, NULL, NULL,
11878 NULL, NULL, NULL, NULL, NULL,
11879 wcroot, local_relpath,
11880 result_pool, scratch_pool));
11881 SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot->sdb,
11882 repos_id, result_pool));
11884 return SVN_NO_ERROR;
11888 /* A helper for scan_addition().
11889 * Compute moved-from information for the node at LOCAL_RELPATH which
11890 * has been determined as having been moved-here.
11891 * If MOVED_FROM_RELPATH is not NULL, set *MOVED_FROM_RELPATH to the
11892 * path of the move-source node in *MOVED_FROM_RELPATH.
11893 * If DELETE_OP_ROOT_RELPATH is not NULL, set *DELETE_OP_ROOT_RELPATH
11894 * to the path of the op-root of the delete-half of the move.
11895 * If moved-from information cannot be derived, set both *MOVED_FROM_RELPATH
11896 * and *DELETE_OP_ROOT_RELPATH to NULL, and return a "copied" status.
11897 * COPY_OPT_ROOT_RELPATH is the relpath of the op-root of the copied-half
11899 static svn_error_t *
11900 get_moved_from_info(const char **moved_from_relpath,
11901 const char **moved_from_op_root_relpath,
11902 const char *moved_to_op_root_relpath,
11904 svn_wc__db_wcroot_t *wcroot,
11905 const char *local_relpath,
11906 apr_pool_t *result_pool,
11907 apr_pool_t *scratch_pool)
11909 svn_sqlite__stmt_t *stmt;
11910 svn_boolean_t have_row;
11912 /* Run a query to get the moved-from path from the DB. */
11913 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11914 STMT_SELECT_MOVED_FROM_RELPATH));
11915 SVN_ERR(svn_sqlite__bindf(stmt, "is",
11916 wcroot->wc_id, moved_to_op_root_relpath));
11917 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11921 /* The move was only recorded at the copy-half, possibly because
11922 * the move operation was interrupted mid-way between the copy
11923 * and the delete. Treat this node as a normal copy. */
11924 if (moved_from_relpath)
11925 *moved_from_relpath = NULL;
11926 if (moved_from_op_root_relpath)
11927 *moved_from_op_root_relpath = NULL;
11929 SVN_ERR(svn_sqlite__reset(stmt));
11930 return SVN_NO_ERROR;
11934 *op_depth = svn_sqlite__column_int(stmt, 1);
11936 if (moved_from_relpath || moved_from_op_root_relpath)
11938 const char *db_delete_op_root_relpath;
11940 /* The moved-from path from the DB is the relpath of
11941 * the op_root of the delete-half of the move. */
11942 db_delete_op_root_relpath = svn_sqlite__column_text(stmt, 0,
11944 if (moved_from_op_root_relpath)
11945 *moved_from_op_root_relpath = db_delete_op_root_relpath;
11947 if (moved_from_relpath)
11949 if (strcmp(moved_to_op_root_relpath, local_relpath) == 0)
11951 /* LOCAL_RELPATH is the op_root of the copied-half of the
11952 * move, so the correct MOVED_FROM_ABSPATH is the op-root
11953 * of the delete-half. */
11954 *moved_from_relpath = db_delete_op_root_relpath;
11958 const char *child_relpath;
11960 /* LOCAL_RELPATH is a child that was copied along with the
11961 * op_root of the copied-half of the move. Construct the
11962 * corresponding path beneath the op_root of the delete-half. */
11964 /* Grab the child path relative to the op_root of the move
11966 child_relpath = svn_relpath_skip_ancestor(
11967 moved_to_op_root_relpath, local_relpath);
11969 SVN_ERR_ASSERT(child_relpath && strlen(child_relpath) > 0);
11971 /* This join is valid because LOCAL_RELPATH has not been moved
11972 * within the copied-half of the move yet -- else, it would
11973 * be its own op_root. */
11974 *moved_from_relpath = svn_relpath_join(db_delete_op_root_relpath,
11981 SVN_ERR(svn_sqlite__reset(stmt));
11983 return SVN_NO_ERROR;
11986 /* The body of scan_addition().
11988 static svn_error_t *
11989 scan_addition_txn(svn_wc__db_status_t *status,
11990 const char **op_root_relpath_p,
11991 const char **repos_relpath,
11992 apr_int64_t *repos_id,
11993 const char **original_repos_relpath,
11994 apr_int64_t *original_repos_id,
11995 svn_revnum_t *original_revision,
11996 const char **moved_from_relpath,
11997 const char **moved_from_op_root_relpath,
11998 int *moved_from_op_depth,
11999 svn_wc__db_wcroot_t *wcroot,
12000 const char *local_relpath,
12001 apr_pool_t *result_pool,
12002 apr_pool_t *scratch_pool)
12004 const char *op_root_relpath;
12005 const char *build_relpath = "";
12007 /* Initialize most of the OUT parameters. Generally, we'll only be filling
12008 in a subset of these, so it is easier to init all up front. Note that
12009 the STATUS parameter will be initialized once we read the status of
12010 the specified node. */
12011 if (op_root_relpath_p)
12012 *op_root_relpath_p = NULL;
12013 if (original_repos_relpath)
12014 *original_repos_relpath = NULL;
12015 if (original_repos_id)
12016 *original_repos_id = INVALID_REPOS_ID;
12017 if (original_revision)
12018 *original_revision = SVN_INVALID_REVNUM;
12019 if (moved_from_relpath)
12020 *moved_from_relpath = NULL;
12021 if (moved_from_op_root_relpath)
12022 *moved_from_op_root_relpath = NULL;
12023 if (moved_from_op_depth)
12024 *moved_from_op_depth = 0;
12027 svn_sqlite__stmt_t *stmt;
12028 svn_boolean_t have_row;
12029 svn_wc__db_status_t presence;
12031 const char *repos_prefix_path = "";
12034 /* ### is it faster to fetch fewer columns? */
12035 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12036 STMT_SELECT_WORKING_NODE));
12037 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
12038 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12042 /* Reset statement before returning */
12043 SVN_ERR(svn_sqlite__reset(stmt));
12045 /* ### maybe we should return a usage error instead? */
12046 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12047 _("The node '%s' was not found."),
12048 path_for_error_message(wcroot,
12053 presence = svn_sqlite__column_token(stmt, 1, presence_map);
12055 /* The starting node should exist normally. */
12056 op_depth = svn_sqlite__column_int(stmt, 0);
12057 if (op_depth == 0 || (presence != svn_wc__db_status_normal
12058 && presence != svn_wc__db_status_incomplete))
12059 /* reset the statement as part of the error generation process */
12060 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
12061 svn_sqlite__reset(stmt),
12062 _("Expected node '%s' to be added."),
12063 path_for_error_message(wcroot,
12067 if (original_revision)
12068 *original_revision = svn_sqlite__column_revnum(stmt, 12);
12070 /* Provide the default status; we'll override as appropriate. */
12073 if (presence == svn_wc__db_status_normal)
12074 *status = svn_wc__db_status_added;
12076 *status = svn_wc__db_status_incomplete;
12080 /* Calculate the op root local path components */
12081 op_root_relpath = local_relpath;
12083 for (i = relpath_depth(local_relpath); i > op_depth; --i)
12085 /* Calculate the path of the operation root */
12086 repos_prefix_path =
12087 svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
12090 op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
12093 if (op_root_relpath_p)
12094 *op_root_relpath_p = apr_pstrdup(result_pool, op_root_relpath);
12096 /* ### This if-statement is quite redundant.
12097 * ### We're checking all these values again within the body anyway.
12098 * ### The body should be broken up appropriately and move into the
12099 * ### outer scope. */
12100 if (original_repos_relpath
12101 || original_repos_id
12102 || (original_revision
12103 && *original_revision == SVN_INVALID_REVNUM)
12105 || moved_from_relpath || moved_from_op_root_relpath)
12107 if (local_relpath != op_root_relpath)
12108 /* requery to get the add/copy root */
12110 SVN_ERR(svn_sqlite__reset(stmt));
12112 SVN_ERR(svn_sqlite__bindf(stmt, "is",
12113 wcroot->wc_id, op_root_relpath));
12114 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12118 /* Reset statement before returning */
12119 SVN_ERR(svn_sqlite__reset(stmt));
12121 /* ### maybe we should return a usage error instead? */
12122 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12123 _("The node '%s' was not found."),
12124 path_for_error_message(wcroot,
12129 if (original_revision
12130 && *original_revision == SVN_INVALID_REVNUM)
12131 *original_revision = svn_sqlite__column_revnum(stmt, 12);
12134 if (original_repos_relpath)
12135 *original_repos_relpath = svn_sqlite__column_text(stmt, 11,
12138 if (!svn_sqlite__column_is_null(stmt, 10)
12140 || original_repos_id
12141 || moved_from_relpath || moved_from_op_root_relpath))
12142 /* If column 10 (original_repos_id) is NULL,
12143 this is a plain add, not a copy or a move */
12145 svn_boolean_t moved_here;
12146 if (original_repos_id)
12147 *original_repos_id = svn_sqlite__column_int64(stmt, 10);
12149 moved_here = svn_sqlite__column_boolean(stmt, 13 /* moved_here */);
12151 *status = moved_here ? svn_wc__db_status_moved_here
12152 : svn_wc__db_status_copied;
12155 && (moved_from_relpath || moved_from_op_root_relpath))
12159 err = get_moved_from_info(moved_from_relpath,
12160 moved_from_op_root_relpath,
12162 moved_from_op_depth,
12163 wcroot, local_relpath,
12168 return svn_error_compose_create(
12169 err, svn_sqlite__reset(stmt));
12175 /* ### This loop here is to skip up to the first node which is a BASE node,
12176 because base_get_info() doesn't accommodate the scenario that
12177 we're looking at here; we found the true op_root, which may be inside
12178 further changed trees. */
12179 if (repos_relpath || repos_id)
12181 const char *base_relpath;
12186 SVN_ERR(svn_sqlite__reset(stmt));
12188 /* Pointing at op_depth, look at the parent */
12189 repos_prefix_path =
12190 svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
12193 op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
12196 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, op_root_relpath));
12197 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12202 op_depth = svn_sqlite__column_int(stmt, 0);
12204 /* Skip to op_depth */
12205 for (i = relpath_depth(op_root_relpath); i > op_depth; i--)
12207 /* Calculate the path of the operation root */
12208 repos_prefix_path =
12209 svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
12213 svn_relpath_dirname(op_root_relpath, scratch_pool);
12217 SVN_ERR(svn_sqlite__reset(stmt));
12219 build_relpath = repos_prefix_path;
12221 /* If we're here, then we have an added/copied/moved (start) node, and
12222 CURRENT_ABSPATH now points to a BASE node. Figure out the repository
12223 information for the current node, and use that to compute the start
12224 node's repository information. */
12225 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12226 &base_relpath, repos_id,
12227 NULL, NULL, NULL, NULL, NULL,
12228 NULL, NULL, NULL, NULL, NULL,
12229 wcroot, op_root_relpath,
12230 scratch_pool, scratch_pool));
12233 *repos_relpath = svn_relpath_join(base_relpath, build_relpath,
12237 SVN_ERR(svn_sqlite__reset(stmt));
12239 /* Postconditions */
12243 SVN_ERR_ASSERT(*status == svn_wc__db_status_added
12244 || *status == svn_wc__db_status_copied
12245 || *status == svn_wc__db_status_incomplete
12246 || *status == svn_wc__db_status_moved_here);
12247 if (*status == svn_wc__db_status_added)
12249 SVN_ERR_ASSERT(!original_repos_relpath
12250 || *original_repos_relpath == NULL);
12251 SVN_ERR_ASSERT(!original_revision
12252 || *original_revision == SVN_INVALID_REVNUM);
12253 SVN_ERR_ASSERT(!original_repos_id
12254 || *original_repos_id == INVALID_REPOS_ID);
12256 /* An upgrade with a missing directory can leave INCOMPLETE working
12257 op-roots. See upgrade_tests.py 29: upgrade with missing replaced dir
12259 else if (*status != svn_wc__db_status_incomplete)
12261 SVN_ERR_ASSERT(!original_repos_relpath
12262 || *original_repos_relpath != NULL);
12263 SVN_ERR_ASSERT(!original_revision
12264 || *original_revision != SVN_INVALID_REVNUM);
12265 SVN_ERR_ASSERT(!original_repos_id
12266 || *original_repos_id != INVALID_REPOS_ID);
12269 SVN_ERR_ASSERT(!op_root_relpath_p || *op_root_relpath_p != NULL);
12272 return SVN_NO_ERROR;
12276 /* Like svn_wc__db_scan_addition(), but with WCROOT+LOCAL_RELPATH instead of
12279 The output value of *ORIGINAL_REPOS_ID will be INVALID_REPOS_ID if there
12280 is no 'copy-from' repository. */
12281 static svn_error_t *
12282 scan_addition(svn_wc__db_status_t *status,
12283 const char **op_root_relpath,
12284 const char **repos_relpath,
12285 apr_int64_t *repos_id,
12286 const char **original_repos_relpath,
12287 apr_int64_t *original_repos_id,
12288 svn_revnum_t *original_revision,
12289 const char **moved_from_relpath,
12290 const char **moved_from_op_root_relpath,
12291 int *moved_from_op_depth,
12292 svn_wc__db_wcroot_t *wcroot,
12293 const char *local_relpath,
12294 apr_pool_t *result_pool,
12295 apr_pool_t *scratch_pool)
12297 SVN_WC__DB_WITH_TXN(
12298 scan_addition_txn(status, op_root_relpath, repos_relpath, repos_id,
12299 original_repos_relpath, original_repos_id,
12300 original_revision, moved_from_relpath,
12301 moved_from_op_root_relpath, moved_from_op_depth,
12302 wcroot, local_relpath, result_pool, scratch_pool),
12304 return SVN_NO_ERROR;
12309 svn_wc__db_scan_addition(svn_wc__db_status_t *status,
12310 const char **op_root_abspath,
12311 const char **repos_relpath,
12312 const char **repos_root_url,
12313 const char **repos_uuid,
12314 const char **original_repos_relpath,
12315 const char **original_root_url,
12316 const char **original_uuid,
12317 svn_revnum_t *original_revision,
12319 const char *local_abspath,
12320 apr_pool_t *result_pool,
12321 apr_pool_t *scratch_pool)
12323 svn_wc__db_wcroot_t *wcroot;
12324 const char *local_relpath;
12325 const char *op_root_relpath = NULL;
12326 apr_int64_t repos_id = INVALID_REPOS_ID;
12327 apr_int64_t original_repos_id = INVALID_REPOS_ID;
12328 apr_int64_t *repos_id_p
12329 = (repos_root_url || repos_uuid) ? &repos_id : NULL;
12330 apr_int64_t *original_repos_id_p
12331 = (original_root_url || original_uuid) ? &original_repos_id : NULL;
12333 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12335 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12336 local_abspath, scratch_pool, scratch_pool));
12337 VERIFY_USABLE_WCROOT(wcroot);
12339 SVN_ERR(scan_addition(status,
12343 repos_relpath, repos_id_p,
12344 original_repos_relpath, original_repos_id_p,
12347 wcroot, local_relpath, result_pool, scratch_pool));
12349 if (op_root_abspath)
12350 *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
12352 /* REPOS_ID must be valid if requested; ORIGINAL_REPOS_ID need not be. */
12353 SVN_ERR_ASSERT(repos_id_p == NULL || repos_id != INVALID_REPOS_ID);
12355 SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot->sdb,
12356 repos_id, result_pool));
12357 SVN_ERR(svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
12358 wcroot->sdb, original_repos_id,
12361 return SVN_NO_ERROR;
12365 svn_wc__db_scan_moved(const char **moved_from_abspath,
12366 const char **op_root_abspath,
12367 const char **op_root_moved_from_abspath,
12368 const char **moved_from_delete_abspath,
12370 const char *local_abspath,
12371 apr_pool_t *result_pool,
12372 apr_pool_t *scratch_pool)
12374 svn_wc__db_wcroot_t *wcroot;
12375 const char *local_relpath;
12376 svn_wc__db_status_t status;
12377 const char *op_root_relpath = NULL;
12378 const char *moved_from_relpath = NULL;
12379 const char *moved_from_op_root_relpath = NULL;
12380 int moved_from_op_depth = -1;
12382 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12384 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12385 local_abspath, scratch_pool, scratch_pool));
12386 VERIFY_USABLE_WCROOT(wcroot);
12388 SVN_ERR(scan_addition(&status,
12395 ? &moved_from_relpath
12397 (op_root_moved_from_abspath
12398 || moved_from_delete_abspath)
12399 ? &moved_from_op_root_relpath
12401 moved_from_delete_abspath
12402 ? &moved_from_op_depth
12404 wcroot, local_relpath, scratch_pool, scratch_pool));
12406 if (status != svn_wc__db_status_moved_here || !moved_from_relpath)
12407 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
12408 _("Path '%s' was not moved here"),
12409 path_for_error_message(wcroot, local_relpath,
12412 if (op_root_abspath)
12413 *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
12416 if (moved_from_abspath)
12417 *moved_from_abspath = svn_dirent_join(wcroot->abspath, moved_from_relpath,
12420 if (op_root_moved_from_abspath)
12421 *op_root_moved_from_abspath = svn_dirent_join(wcroot->abspath,
12422 moved_from_op_root_relpath,
12425 /* The deleted node is either where we moved from, or one of its ancestors */
12426 if (moved_from_delete_abspath)
12428 const char *tmp = moved_from_op_root_relpath;
12430 SVN_ERR_ASSERT(moved_from_op_depth >= 0);
12432 while (relpath_depth(tmp) > moved_from_op_depth)
12433 tmp = svn_relpath_dirname(tmp, scratch_pool);
12435 *moved_from_delete_abspath = svn_dirent_join(wcroot->abspath, tmp,
12439 return SVN_NO_ERROR;
12444 static svn_error_t *
12445 follow_moved_to(apr_array_header_t **moved_tos,
12447 const char *repos_path,
12448 svn_revnum_t revision,
12449 svn_wc__db_wcroot_t *wcroot,
12450 const char *local_relpath,
12451 apr_pool_t *result_pool,
12452 apr_pool_t *scratch_pool)
12454 svn_sqlite__stmt_t *stmt;
12455 svn_boolean_t have_row;
12456 int working_op_depth;
12457 const char *ancestor_relpath, *node_moved_to = NULL;
12460 SVN_ERR_ASSERT((!op_depth && !repos_path) || (op_depth && repos_path));
12462 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12463 STMT_SELECT_OP_DEPTH_MOVED_TO));
12464 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
12466 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12469 working_op_depth = svn_sqlite__column_int(stmt, 0);
12470 node_moved_to = svn_sqlite__column_text(stmt, 1, result_pool);
12473 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12474 if (!have_row || svn_sqlite__column_revnum(stmt, 0))
12475 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
12476 svn_sqlite__reset(stmt),
12477 _("The base node '%s' was not found."),
12478 path_for_error_message(wcroot,
12481 repos_path = svn_sqlite__column_text(stmt, 2, scratch_pool);
12482 revision = svn_sqlite__column_revnum(stmt, 3);
12485 SVN_ERR(svn_sqlite__reset(stmt));
12489 svn_boolean_t have_row2;
12491 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12492 STMT_SELECT_MOVED_HERE));
12493 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, node_moved_to,
12494 relpath_depth(node_moved_to)));
12495 SVN_ERR(svn_sqlite__step(&have_row2, stmt));
12496 if (!have_row2 || !svn_sqlite__column_int(stmt, 0)
12497 || revision != svn_sqlite__column_revnum(stmt, 3)
12498 || strcmp(repos_path, svn_sqlite__column_text(stmt, 2, NULL)))
12499 node_moved_to = NULL;
12500 SVN_ERR(svn_sqlite__reset(stmt));
12505 struct svn_wc__db_moved_to_t *moved_to;
12507 moved_to = apr_palloc(result_pool, sizeof(*moved_to));
12508 moved_to->op_depth = working_op_depth;
12509 moved_to->local_relpath = node_moved_to;
12510 APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
12513 /* A working row with moved_to, or no working row, and we are done. */
12514 if (node_moved_to || !have_row)
12515 return SVN_NO_ERROR;
12517 /* Need to handle being moved via an ancestor. */
12518 ancestor_relpath = local_relpath;
12519 for (i = relpath_depth(local_relpath); i > working_op_depth; --i)
12521 const char *ancestor_moved_to;
12523 ancestor_relpath = svn_relpath_dirname(ancestor_relpath, scratch_pool);
12525 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12526 STMT_SELECT_MOVED_TO));
12527 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, ancestor_relpath,
12528 working_op_depth));
12529 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12530 SVN_ERR_ASSERT(have_row);
12531 ancestor_moved_to = svn_sqlite__column_text(stmt, 0, scratch_pool);
12532 SVN_ERR(svn_sqlite__reset(stmt));
12533 if (ancestor_moved_to)
12536 = svn_relpath_join(ancestor_moved_to,
12537 svn_relpath_skip_ancestor(ancestor_relpath,
12541 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12542 STMT_SELECT_MOVED_HERE));
12543 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, node_moved_to,
12544 relpath_depth(ancestor_moved_to)));
12545 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12547 ancestor_moved_to = NULL;
12548 else if (!svn_sqlite__column_int(stmt, 0))
12550 svn_wc__db_status_t presence
12551 = svn_sqlite__column_token(stmt, 1, presence_map);
12552 if (presence != svn_wc__db_status_not_present)
12553 ancestor_moved_to = NULL;
12556 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12557 if (!have_row && !svn_sqlite__column_int(stmt, 0))
12558 ancestor_moved_to = NULL;
12561 SVN_ERR(svn_sqlite__reset(stmt));
12562 if (!ancestor_moved_to)
12564 /* verify repos_path points back? */
12566 if (ancestor_moved_to)
12568 struct svn_wc__db_moved_to_t *moved_to;
12570 moved_to = apr_palloc(result_pool, sizeof(*moved_to));
12571 moved_to->op_depth = working_op_depth;
12572 moved_to->local_relpath = node_moved_to;
12573 APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
12575 SVN_ERR(follow_moved_to(moved_tos, relpath_depth(ancestor_moved_to),
12576 repos_path, revision, wcroot, node_moved_to,
12577 result_pool, scratch_pool));
12582 return SVN_NO_ERROR;
12586 svn_wc__db_follow_moved_to(apr_array_header_t **moved_tos,
12588 const char *local_abspath,
12589 apr_pool_t *result_pool,
12590 apr_pool_t *scratch_pool)
12592 svn_wc__db_wcroot_t *wcroot;
12593 const char *local_relpath;
12595 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12597 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12598 local_abspath, scratch_pool, scratch_pool));
12599 VERIFY_USABLE_WCROOT(wcroot);
12601 *moved_tos = apr_array_make(result_pool, 0,
12602 sizeof(struct svn_wc__db_moved_to_t *));
12604 /* ### Wrap in a transaction */
12605 SVN_ERR(follow_moved_to(moved_tos, 0, NULL, SVN_INVALID_REVNUM,
12606 wcroot, local_relpath,
12607 result_pool, scratch_pool));
12609 /* ### Convert moved_to to abspath */
12611 return SVN_NO_ERROR;
12614 /* Extract the moved-to information for LOCAL_RELPATH at OP-DEPTH by
12615 examining the lowest working node above OP_DEPTH. The output paths
12616 are NULL if there is no move, otherwise:
12618 *MOVE_DST_RELPATH: the moved-to destination of LOCAL_RELPATH.
12620 *MOVE_DST_OP_ROOT_RELPATH: the moved-to destination of the root of
12621 the move of LOCAL_RELPATH. This may be equal to *MOVE_DST_RELPATH
12622 if LOCAL_RELPATH is the root of the move.
12624 *MOVE_SRC_ROOT_RELPATH: the root of the move source. For moves
12625 inside a delete this will be different from *MOVE_SRC_OP_ROOT_RELPATH.
12627 *MOVE_SRC_OP_ROOT_RELPATH: the root of the source layer that
12628 contains the move. For moves inside deletes this is the root of
12629 the delete, for other moves this is the root of the move.
12631 Given a path A/B/C with A/B moved to X then for A/B/C
12633 MOVE_DST_RELPATH is X/C
12634 MOVE_DST_OP_ROOT_RELPATH is X
12635 MOVE_SRC_ROOT_RELPATH is A/B
12636 MOVE_SRC_OP_ROOT_RELPATH is A/B
12638 If A is then deleted the MOVE_DST_RELPATH, MOVE_DST_OP_ROOT_RELPATH
12639 and MOVE_SRC_ROOT_RELPATH remain the same but MOVE_SRC_OP_ROOT_RELPATH
12642 ### Think about combining with scan_deletion? Also with
12643 ### scan_addition to get moved-to for replaces? Do we need to
12644 ### return the op-root of the move source, i.e. A/B in the example
12647 svn_wc__db_op_depth_moved_to(const char **move_dst_relpath,
12648 const char **move_dst_op_root_relpath,
12649 const char **move_src_root_relpath,
12650 const char **move_src_op_root_relpath,
12652 svn_wc__db_wcroot_t *wcroot,
12653 const char *local_relpath,
12654 apr_pool_t *result_pool,
12655 apr_pool_t *scratch_pool)
12657 svn_sqlite__stmt_t *stmt;
12658 svn_boolean_t have_row;
12659 int delete_op_depth;
12660 const char *relpath = local_relpath;
12662 *move_dst_relpath = *move_dst_op_root_relpath = NULL;
12663 *move_src_root_relpath = *move_src_op_root_relpath = NULL;
12667 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12668 STMT_SELECT_LOWEST_WORKING_NODE));
12669 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath, op_depth));
12670 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12673 delete_op_depth = svn_sqlite__column_int(stmt, 0);
12674 *move_dst_op_root_relpath = svn_sqlite__column_text(stmt, 3,
12676 if (*move_dst_op_root_relpath)
12677 *move_src_root_relpath = apr_pstrdup(result_pool, relpath);
12679 SVN_ERR(svn_sqlite__reset(stmt));
12680 if (!*move_dst_op_root_relpath)
12681 relpath = svn_relpath_dirname(relpath, scratch_pool);
12683 while (!*move_dst_op_root_relpath
12684 && have_row && delete_op_depth <= relpath_depth(relpath));
12686 if (*move_dst_op_root_relpath)
12689 = svn_relpath_join(*move_dst_op_root_relpath,
12690 svn_relpath_skip_ancestor(relpath, local_relpath),
12692 while (delete_op_depth < relpath_depth(relpath))
12693 relpath = svn_relpath_dirname(relpath, scratch_pool);
12694 *move_src_op_root_relpath = apr_pstrdup(result_pool, relpath);
12697 return SVN_NO_ERROR;
12700 /* Public (within libsvn_wc) absolute path version of
12701 svn_wc__db_op_depth_moved_to with the op-depth hard-coded to
12704 svn_wc__db_base_moved_to(const char **move_dst_abspath,
12705 const char **move_dst_op_root_abspath,
12706 const char **move_src_root_abspath,
12707 const char **move_src_op_root_abspath,
12709 const char *local_abspath,
12710 apr_pool_t *result_pool,
12711 apr_pool_t *scratch_pool)
12713 svn_wc__db_wcroot_t *wcroot;
12714 const char *local_relpath;
12715 const char *move_dst_relpath, *move_dst_op_root_relpath;
12716 const char *move_src_root_relpath, *move_src_op_root_relpath;
12718 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12720 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12721 local_abspath, scratch_pool, scratch_pool));
12722 VERIFY_USABLE_WCROOT(wcroot);
12724 SVN_WC__DB_WITH_TXN(svn_wc__db_op_depth_moved_to(&move_dst_relpath,
12725 &move_dst_op_root_relpath,
12726 &move_src_root_relpath,
12727 &move_src_op_root_relpath,
12728 0 /* BASE op-depth */,
12729 wcroot, local_relpath,
12730 scratch_pool, scratch_pool),
12733 if (move_dst_abspath)
12736 ? svn_dirent_join(wcroot->abspath, move_dst_relpath, result_pool)
12739 if (move_dst_op_root_abspath)
12740 *move_dst_op_root_abspath
12741 = move_dst_op_root_relpath
12742 ? svn_dirent_join(wcroot->abspath, move_dst_op_root_relpath, result_pool)
12745 if (move_src_root_abspath)
12746 *move_src_root_abspath
12747 = move_src_root_relpath
12748 ? svn_dirent_join(wcroot->abspath, move_src_root_relpath, result_pool)
12751 if (move_src_op_root_abspath)
12752 *move_src_op_root_abspath
12753 = move_src_op_root_relpath
12754 ? svn_dirent_join(wcroot->abspath, move_src_op_root_relpath, result_pool)
12757 return SVN_NO_ERROR;
12761 svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb,
12762 apr_int64_t *repos_id,
12763 apr_int64_t *wc_id,
12764 svn_wc__db_t *wc_db,
12765 const char *dir_abspath,
12766 const char *repos_root_url,
12767 const char *repos_uuid,
12768 apr_pool_t *scratch_pool)
12770 svn_wc__db_wcroot_t *wcroot;
12772 /* Upgrade is inherently exclusive so specify exclusive locking. */
12773 SVN_ERR(create_db(sdb, repos_id, wc_id, dir_abspath,
12774 repos_root_url, repos_uuid,
12776 NULL, SVN_INVALID_REVNUM, svn_depth_unknown,
12777 TRUE /* exclusive */,
12778 wc_db->state_pool, scratch_pool));
12780 SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
12781 apr_pstrdup(wc_db->state_pool,
12783 *sdb, *wc_id, FORMAT_FROM_SDB,
12784 FALSE /* auto-upgrade */,
12785 FALSE /* enforce_empty_wq */,
12786 wc_db->state_pool, scratch_pool));
12788 /* The WCROOT is complete. Stash it into DB. */
12789 svn_hash_sets(wc_db->dir_data, wcroot->abspath, wcroot);
12791 return SVN_NO_ERROR;
12796 svn_wc__db_upgrade_apply_dav_cache(svn_sqlite__db_t *sdb,
12797 const char *dir_relpath,
12798 apr_hash_t *cache_values,
12799 apr_pool_t *scratch_pool)
12801 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
12803 apr_hash_index_t *hi;
12804 svn_sqlite__stmt_t *stmt;
12806 SVN_ERR(svn_wc__db_util_fetch_wc_id(&wc_id, sdb, iterpool));
12808 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12809 STMT_UPDATE_BASE_NODE_DAV_CACHE));
12811 /* Iterate over all the wcprops, writing each one to the wc_db. */
12812 for (hi = apr_hash_first(scratch_pool, cache_values);
12814 hi = apr_hash_next(hi))
12816 const char *name = svn__apr_hash_index_key(hi);
12817 apr_hash_t *props = svn__apr_hash_index_val(hi);
12818 const char *local_relpath;
12820 svn_pool_clear(iterpool);
12822 local_relpath = svn_relpath_join(dir_relpath, name, iterpool);
12824 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
12825 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, iterpool));
12826 SVN_ERR(svn_sqlite__step_done(stmt));
12829 svn_pool_destroy(iterpool);
12831 return SVN_NO_ERROR;
12836 svn_wc__db_upgrade_apply_props(svn_sqlite__db_t *sdb,
12837 const char *dir_abspath,
12838 const char *local_relpath,
12839 apr_hash_t *base_props,
12840 apr_hash_t *revert_props,
12841 apr_hash_t *working_props,
12842 int original_format,
12844 apr_pool_t *scratch_pool)
12846 svn_sqlite__stmt_t *stmt;
12847 svn_boolean_t have_row;
12848 int top_op_depth = -1;
12849 int below_op_depth = -1;
12850 svn_wc__db_status_t top_presence;
12851 svn_wc__db_status_t below_presence;
12854 /* ### working_props: use set_props_txn.
12855 ### if working_props == NULL, then skip. what if they equal the
12856 ### pristine props? we should probably do the compare here.
12858 ### base props go into WORKING_NODE if avail, otherwise BASE.
12860 ### revert only goes into BASE. (and WORKING better be there!)
12862 Prior to 1.4.0 (ORIGINAL_FORMAT < 8), REVERT_PROPS did not exist. If a
12863 file was deleted, then a copy (potentially with props) was disallowed
12864 and could not replace the deletion. An addition *could* be performed,
12865 but that would never bring its own props.
12867 1.4.0 through 1.4.5 created the concept of REVERT_PROPS, but had a
12868 bug in svn_wc_add_repos_file2() whereby a copy-with-props did NOT
12869 construct a REVERT_PROPS if the target had no props. Thus, reverting
12870 the delete/copy would see no REVERT_PROPS to restore, leaving the
12871 props from the copy source intact, and appearing as if they are (now)
12872 the base props for the previously-deleted file. (wc corruption)
12874 1.4.6 ensured that an empty REVERT_PROPS would be established at all
12875 times. See issue 2530, and r861670 as starting points.
12877 We will use ORIGINAL_FORMAT and SVN_WC__NO_REVERT_FILES to determine
12878 the handling of our inputs, relative to the state of this node.
12881 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_NODE_INFO));
12882 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
12883 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12886 top_op_depth = svn_sqlite__column_int(stmt, 0);
12887 top_presence = svn_sqlite__column_token(stmt, 3, presence_map);
12888 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12891 below_op_depth = svn_sqlite__column_int(stmt, 0);
12892 below_presence = svn_sqlite__column_token(stmt, 3, presence_map);
12895 SVN_ERR(svn_sqlite__reset(stmt));
12897 /* Detect the buggy scenario described above. We cannot upgrade this
12898 working copy if we have no idea where BASE_PROPS should go. */
12899 if (original_format > SVN_WC__NO_REVERT_FILES
12900 && revert_props == NULL
12901 && top_op_depth != -1
12902 && top_presence == svn_wc__db_status_normal
12903 && below_op_depth != -1
12904 && below_presence != svn_wc__db_status_not_present)
12906 /* There should be REVERT_PROPS, so it appears that we just ran into
12907 the described bug. Sigh. */
12908 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
12909 _("The properties of '%s' are in an "
12910 "indeterminate state and cannot be "
12911 "upgraded. See issue #2530."),
12912 svn_dirent_local_style(
12913 svn_dirent_join(dir_abspath, local_relpath,
12914 scratch_pool), scratch_pool));
12917 /* Need at least one row, or two rows if there are revert props */
12918 if (top_op_depth == -1
12919 || (below_op_depth == -1 && revert_props))
12920 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
12921 _("Insufficient NODES rows for '%s'"),
12922 svn_dirent_local_style(
12923 svn_dirent_join(dir_abspath, local_relpath,
12924 scratch_pool), scratch_pool));
12926 /* one row, base props only: upper row gets base props
12927 two rows, base props only: lower row gets base props
12928 two rows, revert props only: lower row gets revert props
12929 two rows, base and revert props: upper row gets base, lower gets revert */
12932 if (revert_props || below_op_depth == -1)
12934 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12935 STMT_UPDATE_NODE_PROPS));
12936 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
12937 wc_id, local_relpath, top_op_depth));
12938 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, base_props, scratch_pool));
12939 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
12941 SVN_ERR_ASSERT(affected_rows == 1);
12944 if (below_op_depth != -1)
12946 apr_hash_t *props = revert_props ? revert_props : base_props;
12948 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12949 STMT_UPDATE_NODE_PROPS));
12950 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
12951 wc_id, local_relpath, below_op_depth));
12952 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
12953 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
12955 SVN_ERR_ASSERT(affected_rows == 1);
12958 /* If there are WORKING_PROPS, then they always go into ACTUAL_NODE. */
12959 if (working_props != NULL
12960 && base_props != NULL)
12962 apr_array_header_t *diffs;
12964 SVN_ERR(svn_prop_diffs(&diffs, working_props, base_props, scratch_pool));
12966 if (diffs->nelts == 0)
12967 working_props = NULL; /* No differences */
12970 if (working_props != NULL)
12972 SVN_ERR(set_actual_props(wc_id, local_relpath, working_props,
12973 sdb, scratch_pool));
12976 return SVN_NO_ERROR;
12980 svn_wc__db_upgrade_insert_external(svn_wc__db_t *db,
12981 const char *local_abspath,
12982 svn_node_kind_t kind,
12983 const char *parent_abspath,
12984 const char *def_local_abspath,
12985 const char *repos_relpath,
12986 const char *repos_root_url,
12987 const char *repos_uuid,
12988 svn_revnum_t def_peg_revision,
12989 svn_revnum_t def_revision,
12990 apr_pool_t *scratch_pool)
12992 svn_wc__db_wcroot_t *wcroot;
12993 const char *def_local_relpath;
12994 svn_sqlite__stmt_t *stmt;
12995 svn_boolean_t have_row;
12996 apr_int64_t repos_id;
12998 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13000 /* We know only of DEF_LOCAL_ABSPATH that it definitely belongs to "this"
13001 * WC, i.e. where the svn:externals prop is set. The external target path
13002 * itself may be "hidden behind" other working copies. */
13003 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &def_local_relpath,
13004 db, def_local_abspath,
13005 scratch_pool, scratch_pool));
13006 VERIFY_USABLE_WCROOT(wcroot);
13009 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13010 STMT_SELECT_REPOSITORY));
13011 SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
13012 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13015 repos_id = svn_sqlite__column_int64(stmt, 0);
13016 SVN_ERR(svn_sqlite__reset(stmt));
13020 /* Need to set up a new repository row. */
13021 SVN_ERR(create_repos_id(&repos_id, repos_root_url, repos_uuid,
13022 wcroot->sdb, scratch_pool));
13025 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13026 STMT_INSERT_EXTERNAL));
13028 /* wc_id, local_relpath, parent_relpath, presence, kind, def_local_relpath,
13029 * repos_id, def_repos_relpath, def_operational_revision, def_revision */
13030 SVN_ERR(svn_sqlite__bindf(stmt, "issstsis",
13032 svn_dirent_skip_ancestor(wcroot->abspath,
13034 svn_dirent_skip_ancestor(wcroot->abspath,
13042 if (SVN_IS_VALID_REVNUM(def_peg_revision))
13043 SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, def_peg_revision));
13045 if (SVN_IS_VALID_REVNUM(def_revision))
13046 SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, def_revision));
13048 SVN_ERR(svn_sqlite__insert(NULL, stmt));
13050 return SVN_NO_ERROR;
13054 svn_wc__db_upgrade_get_repos_id(apr_int64_t *repos_id,
13055 svn_sqlite__db_t *sdb,
13056 const char *repos_root_url,
13057 apr_pool_t *scratch_pool)
13059 svn_sqlite__stmt_t *stmt;
13060 svn_boolean_t have_row;
13062 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_REPOSITORY));
13063 SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
13064 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13067 return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt),
13068 _("Repository '%s' not found in the database"),
13071 *repos_id = svn_sqlite__column_int64(stmt, 0);
13072 return svn_error_trace(svn_sqlite__reset(stmt));
13077 svn_wc__db_wq_add(svn_wc__db_t *db,
13078 const char *wri_abspath,
13079 const svn_skel_t *work_item,
13080 apr_pool_t *scratch_pool)
13082 svn_wc__db_wcroot_t *wcroot;
13083 const char *local_relpath;
13085 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13087 /* Quick exit, if there are no work items to queue up. */
13088 if (work_item == NULL)
13089 return SVN_NO_ERROR;
13091 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13092 wri_abspath, scratch_pool, scratch_pool));
13093 VERIFY_USABLE_WCROOT(wcroot);
13095 /* Add the work item(s) to the WORK_QUEUE. */
13096 return svn_error_trace(add_work_items(wcroot->sdb, work_item,
13100 /* The body of svn_wc__db_wq_fetch_next().
13102 static svn_error_t *
13103 wq_fetch_next(apr_uint64_t *id,
13104 svn_skel_t **work_item,
13105 svn_wc__db_wcroot_t *wcroot,
13106 const char *local_relpath,
13107 apr_uint64_t completed_id,
13108 apr_pool_t *result_pool,
13109 apr_pool_t *scratch_pool)
13111 svn_sqlite__stmt_t *stmt;
13112 svn_boolean_t have_row;
13114 if (completed_id != 0)
13116 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13117 STMT_DELETE_WORK_ITEM));
13118 SVN_ERR(svn_sqlite__bind_int64(stmt, 1, completed_id));
13120 SVN_ERR(svn_sqlite__step_done(stmt));
13123 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13124 STMT_SELECT_WORK_ITEM));
13125 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13137 *id = svn_sqlite__column_int64(stmt, 0);
13139 val = svn_sqlite__column_blob(stmt, 1, &len, result_pool);
13141 *work_item = svn_skel__parse(val, len, result_pool);
13144 return svn_error_trace(svn_sqlite__reset(stmt));
13148 svn_wc__db_wq_fetch_next(apr_uint64_t *id,
13149 svn_skel_t **work_item,
13151 const char *wri_abspath,
13152 apr_uint64_t completed_id,
13153 apr_pool_t *result_pool,
13154 apr_pool_t *scratch_pool)
13156 svn_wc__db_wcroot_t *wcroot;
13157 const char *local_relpath;
13159 SVN_ERR_ASSERT(id != NULL);
13160 SVN_ERR_ASSERT(work_item != NULL);
13161 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13163 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13164 wri_abspath, scratch_pool, scratch_pool));
13165 VERIFY_USABLE_WCROOT(wcroot);
13167 SVN_WC__DB_WITH_TXN(
13168 wq_fetch_next(id, work_item,
13169 wcroot, local_relpath, completed_id,
13170 result_pool, scratch_pool),
13173 return SVN_NO_ERROR;
13176 /* Records timestamp and date for one or more files in wcroot */
13177 static svn_error_t *
13178 wq_record(svn_wc__db_wcroot_t *wcroot,
13179 apr_hash_t *record_map,
13180 apr_pool_t *scratch_pool)
13182 apr_hash_index_t *hi;
13183 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
13185 for (hi = apr_hash_first(scratch_pool, record_map); hi;
13186 hi = apr_hash_next(hi))
13188 const char *local_abspath = svn__apr_hash_index_key(hi);
13189 const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
13190 const char *local_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
13193 svn_pool_clear(iterpool);
13195 if (! local_relpath)
13198 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
13199 dirent->filesize, dirent->mtime,
13203 svn_pool_destroy(iterpool);
13204 return SVN_NO_ERROR;
13208 svn_wc__db_wq_record_and_fetch_next(apr_uint64_t *id,
13209 svn_skel_t **work_item,
13211 const char *wri_abspath,
13212 apr_uint64_t completed_id,
13213 apr_hash_t *record_map,
13214 apr_pool_t *result_pool,
13215 apr_pool_t *scratch_pool)
13217 svn_wc__db_wcroot_t *wcroot;
13218 const char *local_relpath;
13220 SVN_ERR_ASSERT(id != NULL);
13221 SVN_ERR_ASSERT(work_item != NULL);
13222 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13224 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13225 wri_abspath, scratch_pool, scratch_pool));
13226 VERIFY_USABLE_WCROOT(wcroot);
13228 SVN_WC__DB_WITH_TXN(
13229 svn_error_compose_create(
13230 wq_fetch_next(id, work_item,
13231 wcroot, local_relpath, completed_id,
13232 result_pool, scratch_pool),
13233 wq_record(wcroot, record_map, scratch_pool)),
13236 return SVN_NO_ERROR;
13241 /* ### temporary API. remove before release. */
13243 svn_wc__db_temp_get_format(int *format,
13245 const char *local_dir_abspath,
13246 apr_pool_t *scratch_pool)
13248 svn_wc__db_wcroot_t *wcroot;
13249 const char *local_relpath;
13252 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13253 /* ### assert that we were passed a directory? */
13255 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13256 local_dir_abspath, scratch_pool, scratch_pool);
13258 /* If we hit an error examining this directory, then declare this
13259 directory to not be a working copy. */
13262 if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
13263 return svn_error_trace(err);
13264 svn_error_clear(err);
13266 /* Remap the returned error. */
13268 return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
13269 _("'%s' is not a working copy"),
13270 svn_dirent_local_style(local_dir_abspath,
13274 SVN_ERR_ASSERT(wcroot != NULL);
13275 SVN_ERR_ASSERT(wcroot->format >= 1);
13277 *format = wcroot->format;
13279 return SVN_NO_ERROR;
13282 /* ### temporary API. remove before release. */
13283 svn_wc_adm_access_t *
13284 svn_wc__db_temp_get_access(svn_wc__db_t *db,
13285 const char *local_dir_abspath,
13286 apr_pool_t *scratch_pool)
13288 const char *local_relpath;
13289 svn_wc__db_wcroot_t *wcroot;
13292 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13294 /* ### we really need to assert that we were passed a directory. sometimes
13295 ### adm_retrieve_internal is asked about a file, and then it asks us
13296 ### for an access baton for it. we should definitely return NULL, but
13297 ### ideally: the caller would never ask us about a non-directory. */
13299 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13300 db, local_dir_abspath, scratch_pool, scratch_pool);
13303 svn_error_clear(err);
13310 return svn_hash_gets(wcroot->access_cache, local_dir_abspath);
13314 /* ### temporary API. remove before release. */
13316 svn_wc__db_temp_set_access(svn_wc__db_t *db,
13317 const char *local_dir_abspath,
13318 svn_wc_adm_access_t *adm_access,
13319 apr_pool_t *scratch_pool)
13321 const char *local_relpath;
13322 svn_wc__db_wcroot_t *wcroot;
13325 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13326 /* ### assert that we were passed a directory? */
13328 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13329 db, local_dir_abspath, scratch_pool, scratch_pool);
13332 /* We don't even have a wcroot, so just bail. */
13333 svn_error_clear(err);
13337 /* Better not override something already there. */
13338 SVN_ERR_ASSERT_NO_RETURN(
13339 svn_hash_gets(wcroot->access_cache, local_dir_abspath) == NULL
13341 svn_hash_sets(wcroot->access_cache, local_dir_abspath, adm_access);
13345 /* ### temporary API. remove before release. */
13347 svn_wc__db_temp_close_access(svn_wc__db_t *db,
13348 const char *local_dir_abspath,
13349 svn_wc_adm_access_t *adm_access,
13350 apr_pool_t *scratch_pool)
13352 const char *local_relpath;
13353 svn_wc__db_wcroot_t *wcroot;
13355 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13356 /* ### assert that we were passed a directory? */
13358 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13359 local_dir_abspath, scratch_pool, scratch_pool));
13360 svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13362 return SVN_NO_ERROR;
13366 /* ### temporary API. remove before release. */
13368 svn_wc__db_temp_clear_access(svn_wc__db_t *db,
13369 const char *local_dir_abspath,
13370 apr_pool_t *scratch_pool)
13372 const char *local_relpath;
13373 svn_wc__db_wcroot_t *wcroot;
13376 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13377 /* ### assert that we were passed a directory? */
13379 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13380 db, local_dir_abspath, scratch_pool, scratch_pool);
13383 svn_error_clear(err);
13387 svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13392 svn_wc__db_temp_get_all_access(svn_wc__db_t *db,
13393 apr_pool_t *result_pool)
13395 apr_hash_t *result = apr_hash_make(result_pool);
13396 apr_hash_index_t *hi;
13398 for (hi = apr_hash_first(result_pool, db->dir_data);
13400 hi = apr_hash_next(hi))
13402 const svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi);
13404 /* This is highly redundant, 'cause the same WCROOT will appear many
13405 times in dir_data. */
13406 result = apr_hash_overlay(result_pool, result, wcroot->access_cache);
13414 svn_wc__db_temp_borrow_sdb(svn_sqlite__db_t **sdb,
13416 const char *local_dir_abspath,
13417 apr_pool_t *scratch_pool)
13419 svn_wc__db_wcroot_t *wcroot;
13420 const char *local_relpath;
13422 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13424 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13425 local_dir_abspath, scratch_pool, scratch_pool));
13426 VERIFY_USABLE_WCROOT(wcroot);
13428 *sdb = wcroot->sdb;
13430 return SVN_NO_ERROR;
13435 svn_wc__db_read_conflict_victims(const apr_array_header_t **victims,
13437 const char *local_abspath,
13438 apr_pool_t *result_pool,
13439 apr_pool_t *scratch_pool)
13441 svn_wc__db_wcroot_t *wcroot;
13442 const char *local_relpath;
13443 svn_sqlite__stmt_t *stmt;
13444 svn_boolean_t have_row;
13445 apr_array_header_t *new_victims;
13447 /* The parent should be a working copy directory. */
13448 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13449 local_abspath, scratch_pool, scratch_pool));
13450 VERIFY_USABLE_WCROOT(wcroot);
13452 /* ### This will be much easier once we have all conflicts in one
13455 /* Look for text, tree and property conflicts in ACTUAL */
13456 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13457 STMT_SELECT_CONFLICT_VICTIMS));
13458 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13460 new_victims = apr_array_make(result_pool, 0, sizeof(const char *));
13462 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13465 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
13467 APR_ARRAY_PUSH(new_victims, const char *) =
13468 svn_relpath_basename(child_relpath, result_pool);
13470 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13473 SVN_ERR(svn_sqlite__reset(stmt));
13475 *victims = new_victims;
13476 return SVN_NO_ERROR;
13479 /* The body of svn_wc__db_get_conflict_marker_files().
13481 static svn_error_t *
13482 get_conflict_marker_files(apr_hash_t **marker_files_p,
13483 svn_wc__db_wcroot_t *wcroot,
13484 const char *local_relpath,
13486 apr_pool_t *result_pool,
13487 apr_pool_t *scratch_pool)
13489 svn_sqlite__stmt_t *stmt;
13490 svn_boolean_t have_row;
13491 apr_hash_t *marker_files = apr_hash_make(result_pool);
13493 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13494 STMT_SELECT_ACTUAL_NODE));
13495 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13496 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13498 if (have_row && !svn_sqlite__column_is_null(stmt, 2))
13501 const void *data = svn_sqlite__column_blob(stmt, 2, &len, NULL);
13502 svn_skel_t *conflicts;
13503 const apr_array_header_t *markers;
13506 conflicts = svn_skel__parse(data, len, scratch_pool);
13508 /* ### ADD markers to *marker_files */
13509 SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13511 result_pool, scratch_pool));
13513 for (i = 0; markers && (i < markers->nelts); i++)
13515 const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13517 svn_hash_sets(marker_files, marker_abspath, "");
13520 SVN_ERR(svn_sqlite__reset(stmt));
13522 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13523 STMT_SELECT_CONFLICT_VICTIMS));
13524 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13525 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13530 const void *data = svn_sqlite__column_blob(stmt, 1, &len, NULL);
13532 const apr_array_header_t *markers;
13537 svn_skel_t *conflicts;
13538 conflicts = svn_skel__parse(data, len, scratch_pool);
13540 SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13542 result_pool, scratch_pool));
13544 for (i = 0; markers && (i < markers->nelts); i++)
13546 const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13548 svn_hash_sets(marker_files, marker_abspath, "");
13552 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13555 if (apr_hash_count(marker_files))
13556 *marker_files_p = marker_files;
13558 *marker_files_p = NULL;
13560 return svn_error_trace(svn_sqlite__reset(stmt));
13564 svn_wc__db_get_conflict_marker_files(apr_hash_t **marker_files,
13566 const char *local_abspath,
13567 apr_pool_t *result_pool,
13568 apr_pool_t *scratch_pool)
13570 svn_wc__db_wcroot_t *wcroot;
13571 const char *local_relpath;
13573 /* The parent should be a working copy directory. */
13574 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13575 local_abspath, scratch_pool, scratch_pool));
13576 VERIFY_USABLE_WCROOT(wcroot);
13578 SVN_WC__DB_WITH_TXN(
13579 get_conflict_marker_files(marker_files, wcroot, local_relpath, db,
13580 result_pool, scratch_pool),
13583 return SVN_NO_ERROR;
13588 svn_wc__db_read_conflict(svn_skel_t **conflict,
13590 const char *local_abspath,
13591 apr_pool_t *result_pool,
13592 apr_pool_t *scratch_pool)
13594 svn_wc__db_wcroot_t *wcroot;
13595 const char *local_relpath;
13597 /* The parent should be a working copy directory. */
13598 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13599 local_abspath, scratch_pool, scratch_pool));
13600 VERIFY_USABLE_WCROOT(wcroot);
13602 return svn_error_trace(svn_wc__db_read_conflict_internal(conflict, wcroot,
13609 svn_wc__db_read_conflict_internal(svn_skel_t **conflict,
13610 svn_wc__db_wcroot_t *wcroot,
13611 const char *local_relpath,
13612 apr_pool_t *result_pool,
13613 apr_pool_t *scratch_pool)
13615 svn_sqlite__stmt_t *stmt;
13616 svn_boolean_t have_row;
13618 /* Check if we have a conflict in ACTUAL */
13619 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13620 STMT_SELECT_ACTUAL_NODE));
13621 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13623 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13627 /* Do this while stmt is still open to avoid closing the sqlite
13628 transaction and then reopening. */
13629 svn_sqlite__stmt_t *stmt_node;
13632 err = svn_sqlite__get_statement(&stmt_node, wcroot->sdb,
13633 STMT_SELECT_NODE_INFO);
13638 err = svn_sqlite__bindf(stmt_node, "is", wcroot->wc_id,
13642 err = svn_sqlite__step(&have_row, stmt_node);
13645 err = svn_error_compose_create(err,
13646 svn_sqlite__reset(stmt_node));
13648 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
13653 return SVN_NO_ERROR;
13656 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13657 _("The node '%s' was not found."),
13658 path_for_error_message(wcroot,
13664 apr_size_t cfl_len;
13665 const void *cfl_data;
13667 /* svn_skel__parse doesn't copy data, so store in result_pool */
13668 cfl_data = svn_sqlite__column_blob(stmt, 2, &cfl_len, result_pool);
13671 *conflict = svn_skel__parse(cfl_data, cfl_len, result_pool);
13675 return svn_error_trace(svn_sqlite__reset(stmt));
13681 svn_wc__db_read_kind(svn_node_kind_t *kind,
13683 const char *local_abspath,
13684 svn_boolean_t allow_missing,
13685 svn_boolean_t show_deleted,
13686 svn_boolean_t show_hidden,
13687 apr_pool_t *scratch_pool)
13689 svn_wc__db_wcroot_t *wcroot;
13690 const char *local_relpath;
13691 svn_sqlite__stmt_t *stmt_info;
13692 svn_boolean_t have_info;
13694 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13696 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13697 local_abspath, scratch_pool, scratch_pool));
13698 VERIFY_USABLE_WCROOT(wcroot);
13700 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
13701 STMT_SELECT_NODE_INFO));
13702 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
13703 SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
13709 *kind = svn_node_unknown;
13710 SVN_ERR(svn_sqlite__reset(stmt_info));
13711 return SVN_NO_ERROR;
13715 SVN_ERR(svn_sqlite__reset(stmt_info));
13716 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13717 _("The node '%s' was not found."),
13718 path_for_error_message(wcroot,
13724 if (!(show_deleted && show_hidden))
13726 int op_depth = svn_sqlite__column_int(stmt_info, 0);
13727 svn_boolean_t report_none = FALSE;
13728 svn_wc__db_status_t status = svn_sqlite__column_token(stmt_info, 3,
13732 SVN_ERR(convert_to_working_status(&status, status));
13736 case svn_wc__db_status_not_present:
13737 if (! (show_hidden && show_deleted))
13738 report_none = TRUE;
13740 case svn_wc__db_status_excluded:
13741 case svn_wc__db_status_server_excluded:
13743 report_none = TRUE;
13745 case svn_wc__db_status_deleted:
13746 if (! show_deleted)
13747 report_none = TRUE;
13755 *kind = svn_node_none;
13756 return svn_error_trace(svn_sqlite__reset(stmt_info));
13760 *kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
13762 return svn_error_trace(svn_sqlite__reset(stmt_info));
13767 svn_wc__db_node_hidden(svn_boolean_t *hidden,
13769 const char *local_abspath,
13770 apr_pool_t *scratch_pool)
13772 svn_wc__db_wcroot_t *wcroot;
13773 const char *local_relpath;
13774 svn_wc__db_status_t status;
13776 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13778 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13779 local_abspath, scratch_pool, scratch_pool));
13780 VERIFY_USABLE_WCROOT(wcroot);
13782 SVN_ERR(read_info(&status, NULL, NULL, NULL, NULL, NULL,
13783 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13784 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13786 wcroot, local_relpath,
13787 scratch_pool, scratch_pool));
13789 *hidden = (status == svn_wc__db_status_server_excluded
13790 || status == svn_wc__db_status_not_present
13791 || status == svn_wc__db_status_excluded);
13793 return SVN_NO_ERROR;
13798 svn_wc__db_is_wcroot(svn_boolean_t *is_wcroot,
13800 const char *local_abspath,
13801 apr_pool_t *scratch_pool)
13803 svn_wc__db_wcroot_t *wcroot;
13804 const char *local_relpath;
13806 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13808 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13809 local_abspath, scratch_pool, scratch_pool));
13810 VERIFY_USABLE_WCROOT(wcroot);
13812 if (*local_relpath != '\0')
13814 *is_wcroot = FALSE; /* Node is a file, or has a parent directory within
13816 return SVN_NO_ERROR;
13821 return SVN_NO_ERROR;
13824 /* Find a node's kind and whether it is switched, putting the outputs in
13825 * *IS_SWITCHED and *KIND. Either of the outputs may be NULL if not wanted.
13827 static svn_error_t *
13828 db_is_switched(svn_boolean_t *is_switched,
13829 svn_node_kind_t *kind,
13830 svn_wc__db_wcroot_t *wcroot,
13831 const char *local_relpath,
13832 apr_pool_t *scratch_pool)
13834 svn_wc__db_status_t status;
13835 apr_int64_t repos_id;
13836 const char *repos_relpath;
13838 const char *parent_local_relpath;
13839 apr_int64_t parent_repos_id;
13840 const char *parent_repos_relpath;
13842 SVN_ERR_ASSERT(*local_relpath != '\0'); /* Handled in wrapper */
13844 SVN_ERR(read_info(&status, kind, NULL, &repos_relpath, &repos_id, NULL,
13845 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13846 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13847 wcroot, local_relpath, scratch_pool, scratch_pool));
13849 if (status == svn_wc__db_status_server_excluded
13850 || status == svn_wc__db_status_excluded
13851 || status == svn_wc__db_status_not_present)
13853 return svn_error_createf(
13854 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13855 _("The node '%s' was not found."),
13856 path_for_error_message(wcroot, local_relpath,
13859 else if (! repos_relpath)
13861 /* Node is shadowed; easy out */
13863 *is_switched = FALSE;
13865 return SVN_NO_ERROR;
13869 return SVN_NO_ERROR;
13871 svn_relpath_split(&parent_local_relpath, &name, local_relpath, scratch_pool);
13873 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
13874 &parent_repos_relpath,
13875 &parent_repos_id, NULL, NULL, NULL,
13876 NULL, NULL, NULL, NULL, NULL,
13878 wcroot, parent_local_relpath,
13879 scratch_pool, scratch_pool));
13881 if (repos_id != parent_repos_id)
13882 *is_switched = TRUE;
13885 const char *expected_relpath;
13887 expected_relpath = svn_relpath_join(parent_repos_relpath, name,
13890 *is_switched = (strcmp(expected_relpath, repos_relpath) != 0);
13893 return SVN_NO_ERROR;
13897 svn_wc__db_is_switched(svn_boolean_t *is_wcroot,
13898 svn_boolean_t *is_switched,
13899 svn_node_kind_t *kind,
13901 const char *local_abspath,
13902 apr_pool_t *scratch_pool)
13904 svn_wc__db_wcroot_t *wcroot;
13905 const char *local_relpath;
13907 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13909 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13910 local_abspath, scratch_pool, scratch_pool));
13911 VERIFY_USABLE_WCROOT(wcroot);
13914 *is_switched = FALSE;
13916 if (*local_relpath == '\0')
13923 *kind = svn_node_dir;
13924 return SVN_NO_ERROR;
13928 *is_wcroot = FALSE;
13930 if (! is_switched && ! kind)
13931 return SVN_NO_ERROR;
13933 SVN_WC__DB_WITH_TXN(
13934 db_is_switched(is_switched, kind, wcroot, local_relpath, scratch_pool),
13936 return SVN_NO_ERROR;
13941 svn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath,
13943 const char *wri_abspath,
13944 apr_pool_t *result_pool,
13945 apr_pool_t *scratch_pool)
13947 svn_wc__db_wcroot_t *wcroot;
13948 const char *local_relpath;
13950 SVN_ERR_ASSERT(temp_dir_abspath != NULL);
13951 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13953 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13954 wri_abspath, scratch_pool, scratch_pool));
13955 VERIFY_USABLE_WCROOT(wcroot);
13957 *temp_dir_abspath = svn_dirent_join_many(result_pool,
13959 svn_wc_get_adm_dir(scratch_pool),
13960 WCROOT_TEMPDIR_RELPATH,
13962 return SVN_NO_ERROR;
13966 /* Helper for wclock_obtain_cb() to steal an existing lock */
13967 static svn_error_t *
13968 wclock_steal(svn_wc__db_wcroot_t *wcroot,
13969 const char *local_relpath,
13970 apr_pool_t *scratch_pool)
13972 svn_sqlite__stmt_t *stmt;
13974 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_WC_LOCK));
13975 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13977 SVN_ERR(svn_sqlite__step_done(stmt));
13979 return SVN_NO_ERROR;
13983 /* The body of svn_wc__db_wclock_obtain().
13985 static svn_error_t *
13986 wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot,
13987 const char *local_relpath,
13988 int levels_to_lock,
13989 svn_boolean_t steal_lock,
13990 apr_pool_t *scratch_pool)
13992 svn_sqlite__stmt_t *stmt;
13994 const char *lock_relpath;
13997 svn_boolean_t got_row;
13999 svn_wc__db_wclock_t lock;
14001 /* Upgrade locks the root before the node exists. Apart from that
14002 the root node always exists so we will just skip the check.
14004 ### Perhaps the lock for upgrade should be created when the db is
14005 created? 1.6 used to lock .svn on creation. */
14006 if (local_relpath[0])
14008 svn_boolean_t exists;
14010 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
14012 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14013 _("The node '%s' was not found."),
14014 path_for_error_message(wcroot,
14019 /* Check if there are nodes locked below the new lock root */
14020 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_FIND_WC_LOCK));
14021 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14023 lock_depth = relpath_depth(local_relpath);
14024 max_depth = lock_depth + levels_to_lock;
14026 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14030 svn_boolean_t own_lock;
14032 lock_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
14034 /* If we are not locking with depth infinity, check if this lock
14035 voids our lock request */
14036 if (levels_to_lock >= 0
14037 && relpath_depth(lock_relpath) > max_depth)
14039 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14043 /* Check if we are the lock owner, because we should be able to
14044 extend our lock. */
14045 err = wclock_owns_lock(&own_lock, wcroot, lock_relpath,
14046 TRUE, scratch_pool);
14049 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14051 if (!own_lock && !steal_lock)
14053 SVN_ERR(svn_sqlite__reset(stmt));
14054 err = svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
14055 _("'%s' is already locked."),
14056 path_for_error_message(wcroot,
14059 return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14060 _("Working copy '%s' locked."),
14061 path_for_error_message(wcroot,
14065 else if (!own_lock)
14067 err = wclock_steal(wcroot, lock_relpath, scratch_pool);
14070 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14073 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14076 SVN_ERR(svn_sqlite__reset(stmt));
14079 SVN_ERR(wclock_steal(wcroot, local_relpath, scratch_pool));
14081 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_WC_LOCK));
14082 lock_relpath = local_relpath;
14086 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, lock_relpath));
14088 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14092 int levels = svn_sqlite__column_int(stmt, 0);
14094 levels += relpath_depth(lock_relpath);
14096 SVN_ERR(svn_sqlite__reset(stmt));
14098 if (levels == -1 || levels >= lock_depth)
14101 err = svn_error_createf(
14102 SVN_ERR_WC_LOCKED, NULL,
14103 _("'%s' is already locked."),
14104 svn_dirent_local_style(
14105 svn_dirent_join(wcroot->abspath,
14109 return svn_error_createf(
14110 SVN_ERR_WC_LOCKED, err,
14111 _("Working copy '%s' locked."),
14112 path_for_error_message(wcroot,
14117 break; /* There can't be interesting locks on higher nodes */
14120 SVN_ERR(svn_sqlite__reset(stmt));
14122 if (!*lock_relpath)
14125 lock_relpath = svn_relpath_dirname(lock_relpath, scratch_pool);
14128 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_WC_LOCK));
14129 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14131 err = svn_sqlite__insert(NULL, stmt);
14133 return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14134 _("Working copy '%s' locked"),
14135 path_for_error_message(wcroot,
14139 /* And finally store that we obtained the lock */
14140 lock.local_relpath = apr_pstrdup(wcroot->owned_locks->pool, local_relpath);
14141 lock.levels = levels_to_lock;
14142 APR_ARRAY_PUSH(wcroot->owned_locks, svn_wc__db_wclock_t) = lock;
14144 return SVN_NO_ERROR;
14149 svn_wc__db_wclock_obtain(svn_wc__db_t *db,
14150 const char *local_abspath,
14151 int levels_to_lock,
14152 svn_boolean_t steal_lock,
14153 apr_pool_t *scratch_pool)
14155 svn_wc__db_wcroot_t *wcroot;
14156 const char *local_relpath;
14158 SVN_ERR_ASSERT(levels_to_lock >= -1);
14159 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14161 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14163 scratch_pool, scratch_pool));
14164 VERIFY_USABLE_WCROOT(wcroot);
14169 int depth = relpath_depth(local_relpath);
14171 for (i = 0; i < wcroot->owned_locks->nelts; i++)
14173 svn_wc__db_wclock_t* lock = &APR_ARRAY_IDX(wcroot->owned_locks,
14174 i, svn_wc__db_wclock_t);
14176 if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14177 && (lock->levels == -1
14178 || (lock->levels + relpath_depth(lock->local_relpath))
14181 return svn_error_createf(
14182 SVN_ERR_WC_LOCKED, NULL,
14183 _("'%s' is already locked via '%s'."),
14184 svn_dirent_local_style(local_abspath, scratch_pool),
14185 path_for_error_message(wcroot, lock->local_relpath,
14191 SVN_WC__DB_WITH_TXN(
14192 wclock_obtain_cb(wcroot, local_relpath, levels_to_lock, steal_lock,
14195 return SVN_NO_ERROR;
14199 /* The body of svn_wc__db_wclock_find_root() and svn_wc__db_wclocked(). */
14200 static svn_error_t *
14201 find_wclock(const char **lock_relpath,
14202 svn_wc__db_wcroot_t *wcroot,
14203 const char *dir_relpath,
14204 apr_pool_t *result_pool,
14205 apr_pool_t *scratch_pool)
14207 svn_sqlite__stmt_t *stmt;
14208 svn_boolean_t have_row;
14209 int dir_depth = relpath_depth(dir_relpath);
14210 const char *first_relpath;
14212 /* Check for locks on all directories that might be ancestors.
14213 As our new apis only use recursive locks the number of locks stored
14214 in the DB will be very low */
14215 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14216 STMT_SELECT_ANCESTOR_WCLOCKS));
14218 /* Get the top level relpath to reduce the worst case number of results
14219 to the number of directories below this node plus two.
14220 (1: the node itself and 2: the wcroot). */
14221 first_relpath = strchr(dir_relpath, '/');
14223 if (first_relpath != NULL)
14224 first_relpath = apr_pstrndup(scratch_pool, dir_relpath,
14225 first_relpath - dir_relpath);
14227 first_relpath = dir_relpath;
14229 SVN_ERR(svn_sqlite__bindf(stmt, "iss",
14234 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14238 const char *relpath = svn_sqlite__column_text(stmt, 0, NULL);
14240 if (svn_relpath_skip_ancestor(relpath, dir_relpath))
14242 int locked_levels = svn_sqlite__column_int(stmt, 1);
14243 int row_depth = relpath_depth(relpath);
14245 if (locked_levels == -1
14246 || locked_levels + row_depth >= dir_depth)
14248 *lock_relpath = apr_pstrdup(result_pool, relpath);
14249 SVN_ERR(svn_sqlite__reset(stmt));
14250 return SVN_NO_ERROR;
14254 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14257 *lock_relpath = NULL;
14259 return svn_error_trace(svn_sqlite__reset(stmt));
14262 static svn_error_t *
14263 is_wclocked(svn_boolean_t *locked,
14264 svn_wc__db_wcroot_t *wcroot,
14265 const char *dir_relpath,
14266 apr_pool_t *scratch_pool)
14268 const char *lock_relpath;
14270 SVN_ERR(find_wclock(&lock_relpath, wcroot, dir_relpath,
14271 scratch_pool, scratch_pool));
14272 *locked = (lock_relpath != NULL);
14273 return SVN_NO_ERROR;
14278 svn_wc__db_wclock_find_root(const char **lock_abspath,
14280 const char *local_abspath,
14281 apr_pool_t *result_pool,
14282 apr_pool_t *scratch_pool)
14284 svn_wc__db_wcroot_t *wcroot;
14285 const char *local_relpath;
14286 const char *lock_relpath;
14288 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14289 local_abspath, scratch_pool, scratch_pool));
14290 VERIFY_USABLE_WCROOT(wcroot);
14292 SVN_WC__DB_WITH_TXN(
14293 find_wclock(&lock_relpath, wcroot, local_relpath,
14294 scratch_pool, scratch_pool),
14298 *lock_abspath = NULL;
14300 SVN_ERR(svn_wc__db_from_relpath(lock_abspath, db, wcroot->abspath,
14301 lock_relpath, result_pool, scratch_pool));
14302 return SVN_NO_ERROR;
14307 svn_wc__db_wclocked(svn_boolean_t *locked,
14309 const char *local_abspath,
14310 apr_pool_t *scratch_pool)
14312 svn_wc__db_wcroot_t *wcroot;
14313 const char *local_relpath;
14315 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14316 local_abspath, scratch_pool, scratch_pool));
14317 VERIFY_USABLE_WCROOT(wcroot);
14319 SVN_WC__DB_WITH_TXN(
14320 is_wclocked(locked, wcroot, local_relpath, scratch_pool),
14323 return SVN_NO_ERROR;
14328 svn_wc__db_wclock_release(svn_wc__db_t *db,
14329 const char *local_abspath,
14330 apr_pool_t *scratch_pool)
14332 svn_sqlite__stmt_t *stmt;
14333 svn_wc__db_wcroot_t *wcroot;
14334 const char *local_relpath;
14336 apr_array_header_t *owned_locks;
14338 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14339 local_abspath, scratch_pool, scratch_pool));
14341 VERIFY_USABLE_WCROOT(wcroot);
14343 /* First check and remove the owns-lock information as failure in
14344 removing the db record implies that we have to steal the lock later. */
14345 owned_locks = wcroot->owned_locks;
14346 for (i = 0; i < owned_locks->nelts; i++)
14348 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14349 svn_wc__db_wclock_t);
14351 if (strcmp(lock->local_relpath, local_relpath) == 0)
14355 if (i >= owned_locks->nelts)
14356 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
14357 _("Working copy not locked at '%s'."),
14358 svn_dirent_local_style(local_abspath,
14361 if (i < owned_locks->nelts)
14363 owned_locks->nelts--;
14365 /* Move the last item in the array to the deleted place */
14366 if (owned_locks->nelts > 0)
14367 APR_ARRAY_IDX(owned_locks, i, svn_wc__db_wclock_t) =
14368 APR_ARRAY_IDX(owned_locks, owned_locks->nelts, svn_wc__db_wclock_t);
14371 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14372 STMT_DELETE_WC_LOCK));
14374 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14376 SVN_ERR(svn_sqlite__step_done(stmt));
14378 return SVN_NO_ERROR;
14382 /* Like svn_wc__db_wclock_owns_lock() but taking WCROOT+LOCAL_RELPATH instead
14383 of DB+LOCAL_ABSPATH. */
14384 static svn_error_t *
14385 wclock_owns_lock(svn_boolean_t *own_lock,
14386 svn_wc__db_wcroot_t *wcroot,
14387 const char *local_relpath,
14388 svn_boolean_t exact,
14389 apr_pool_t *scratch_pool)
14391 apr_array_header_t *owned_locks;
14396 owned_locks = wcroot->owned_locks;
14397 lock_level = relpath_depth(local_relpath);
14401 for (i = 0; i < owned_locks->nelts; i++)
14403 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14404 svn_wc__db_wclock_t);
14406 if (strcmp(lock->local_relpath, local_relpath) == 0)
14409 return SVN_NO_ERROR;
14415 for (i = 0; i < owned_locks->nelts; i++)
14417 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14418 svn_wc__db_wclock_t);
14420 if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14421 && (lock->levels == -1
14422 || ((relpath_depth(lock->local_relpath) + lock->levels)
14426 return SVN_NO_ERROR;
14431 return SVN_NO_ERROR;
14436 svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
14438 const char *local_abspath,
14439 svn_boolean_t exact,
14440 apr_pool_t *scratch_pool)
14442 svn_wc__db_wcroot_t *wcroot;
14443 const char *local_relpath;
14445 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14446 local_abspath, scratch_pool, scratch_pool));
14449 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
14450 _("The node '%s' was not found."),
14451 svn_dirent_local_style(local_abspath,
14454 VERIFY_USABLE_WCROOT(wcroot);
14456 SVN_ERR(wclock_owns_lock(own_lock, wcroot, local_relpath, exact,
14459 return SVN_NO_ERROR;
14462 /* The body of svn_wc__db_temp_op_end_directory_update().
14464 static svn_error_t *
14465 end_directory_update(svn_wc__db_wcroot_t *wcroot,
14466 const char *local_relpath,
14467 apr_pool_t *scratch_pool)
14469 svn_sqlite__stmt_t *stmt;
14470 svn_wc__db_status_t base_status;
14472 SVN_ERR(svn_wc__db_base_get_info_internal(&base_status, NULL, NULL, NULL,
14473 NULL, NULL, NULL, NULL, NULL,
14474 NULL, NULL, NULL, NULL, NULL, NULL,
14475 wcroot, local_relpath,
14476 scratch_pool, scratch_pool));
14478 if (base_status == svn_wc__db_status_normal)
14479 return SVN_NO_ERROR;
14481 SVN_ERR_ASSERT(base_status == svn_wc__db_status_incomplete);
14483 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14484 STMT_UPDATE_NODE_BASE_PRESENCE));
14485 SVN_ERR(svn_sqlite__bindf(stmt, "ist", wcroot->wc_id, local_relpath,
14486 presence_map, svn_wc__db_status_normal));
14487 SVN_ERR(svn_sqlite__step_done(stmt));
14489 return SVN_NO_ERROR;
14493 svn_wc__db_temp_op_end_directory_update(svn_wc__db_t *db,
14494 const char *local_dir_abspath,
14495 apr_pool_t *scratch_pool)
14497 svn_wc__db_wcroot_t *wcroot;
14498 const char *local_relpath;
14500 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
14502 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14503 local_dir_abspath, scratch_pool, scratch_pool));
14504 VERIFY_USABLE_WCROOT(wcroot);
14506 SVN_WC__DB_WITH_TXN(
14507 end_directory_update(wcroot, local_relpath, scratch_pool),
14510 SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_empty,
14513 return SVN_NO_ERROR;
14517 /* The body of svn_wc__db_temp_op_start_directory_update().
14519 static svn_error_t *
14520 start_directory_update_txn(svn_wc__db_wcroot_t *wcroot,
14521 const char *local_relpath,
14522 const char *new_repos_relpath,
14523 svn_revnum_t new_rev,
14524 apr_pool_t *scratch_pool)
14526 svn_sqlite__stmt_t *stmt;
14528 /* Note: In the majority of calls, the repos_relpath is unchanged. */
14529 /* ### TODO: Maybe check if we can make repos_relpath NULL. */
14530 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14531 STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH));
14533 SVN_ERR(svn_sqlite__bindf(stmt, "istrs",
14536 presence_map, svn_wc__db_status_incomplete,
14538 new_repos_relpath));
14539 SVN_ERR(svn_sqlite__step_done(stmt));
14541 return SVN_NO_ERROR;
14546 svn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db,
14547 const char *local_abspath,
14548 const char *new_repos_relpath,
14549 svn_revnum_t new_rev,
14550 apr_pool_t *scratch_pool)
14552 svn_wc__db_wcroot_t *wcroot;
14553 const char *local_relpath;
14555 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14556 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_rev));
14557 SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
14559 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14560 local_abspath, scratch_pool, scratch_pool));
14561 VERIFY_USABLE_WCROOT(wcroot);
14563 SVN_WC__DB_WITH_TXN(
14564 start_directory_update_txn(wcroot, local_relpath,
14565 new_repos_relpath, new_rev, scratch_pool),
14568 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
14570 return SVN_NO_ERROR;
14574 /* The body of svn_wc__db_temp_op_make_copy(). This is
14575 used by the update editor when deleting a base node tree would be a
14576 tree-conflict because there are changes to subtrees. This function
14577 inserts a copy of the base node tree below any existing working
14578 subtrees. Given a tree:
14583 A/B normal - normal
14584 A/B/C normal - base-del normal
14585 A/F normal - normal
14586 A/F/G normal - normal
14587 A/F/H normal - base-deleted normal
14588 A/F/E normal - not-present
14592 This function adds layers to A and some of its descendants in an attempt
14593 to make the working copy look like as if it were a copy of the BASE nodes.
14598 A/B normal norm norm
14599 A/B/C normal norm base-del normal
14600 A/F normal norm norm
14601 A/F/G normal norm norm
14602 A/F/H normal norm not-pres
14603 A/F/E normal norm base-del
14605 A/X/Y incomplete incomplete
14607 static svn_error_t *
14608 make_copy_txn(svn_wc__db_wcroot_t *wcroot,
14609 const char *local_relpath,
14611 const svn_skel_t *conflicts,
14612 const svn_skel_t *work_items,
14613 apr_pool_t *scratch_pool)
14615 svn_sqlite__stmt_t *stmt;
14616 svn_boolean_t have_row;
14617 svn_boolean_t add_working_base_deleted = FALSE;
14618 svn_boolean_t remove_working = FALSE;
14619 const apr_array_header_t *children;
14620 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
14623 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14624 STMT_SELECT_LOWEST_WORKING_NODE));
14625 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
14626 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14630 svn_wc__db_status_t working_status;
14631 int working_op_depth;
14633 working_status = svn_sqlite__column_token(stmt, 1, presence_map);
14634 working_op_depth = svn_sqlite__column_int(stmt, 0);
14635 SVN_ERR(svn_sqlite__reset(stmt));
14637 SVN_ERR_ASSERT(working_status == svn_wc__db_status_normal
14638 || working_status == svn_wc__db_status_base_deleted
14639 || working_status == svn_wc__db_status_not_present
14640 || working_status == svn_wc__db_status_incomplete);
14642 /* Only change nodes in the layers where we are creating the copy.
14643 Deletes in higher layers will just apply to the copy */
14644 if (working_op_depth <= op_depth)
14646 add_working_base_deleted = TRUE;
14648 if (working_status == svn_wc__db_status_base_deleted)
14649 remove_working = TRUE;
14653 SVN_ERR(svn_sqlite__reset(stmt));
14655 if (remove_working)
14657 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14658 STMT_DELETE_LOWEST_WORKING_NODE));
14659 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14660 SVN_ERR(svn_sqlite__step_done(stmt));
14663 if (add_working_base_deleted)
14665 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14666 STMT_INSERT_DELETE_FROM_BASE));
14667 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14669 SVN_ERR(svn_sqlite__step_done(stmt));
14673 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14674 STMT_INSERT_WORKING_NODE_FROM_BASE_COPY));
14675 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14677 SVN_ERR(svn_sqlite__step_done(stmt));
14680 /* Get the BASE children, as WORKING children don't need modifications */
14681 SVN_ERR(gather_repo_children(&children, wcroot, local_relpath,
14682 0, scratch_pool, iterpool));
14684 for (i = 0; i < children->nelts; i++)
14686 const char *name = APR_ARRAY_IDX(children, i, const char *);
14687 const char *copy_relpath;
14689 svn_pool_clear(iterpool);
14691 copy_relpath = svn_relpath_join(local_relpath, name, iterpool);
14693 SVN_ERR(make_copy_txn(wcroot, copy_relpath, op_depth, NULL, NULL,
14697 SVN_ERR(flush_entries(wcroot, svn_dirent_join(wcroot->abspath, local_relpath,
14699 svn_depth_empty, iterpool));
14702 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
14703 conflicts, iterpool));
14705 SVN_ERR(add_work_items(wcroot->sdb, work_items, iterpool));
14707 svn_pool_destroy(iterpool);
14709 return SVN_NO_ERROR;
14714 svn_wc__db_op_make_copy(svn_wc__db_t *db,
14715 const char *local_abspath,
14716 const svn_skel_t *conflicts,
14717 const svn_skel_t *work_items,
14718 apr_pool_t *scratch_pool)
14720 svn_wc__db_wcroot_t *wcroot;
14721 const char *local_relpath;
14722 svn_sqlite__stmt_t *stmt;
14723 svn_boolean_t have_row;
14725 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14727 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14728 local_abspath, scratch_pool, scratch_pool));
14729 VERIFY_USABLE_WCROOT(wcroot);
14731 /* The update editor is supposed to call this function when there is
14732 no working node for LOCAL_ABSPATH. */
14733 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14734 STMT_SELECT_WORKING_NODE));
14735 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14736 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14737 SVN_ERR(svn_sqlite__reset(stmt));
14739 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
14740 _("Modification of '%s' already exists"),
14741 path_for_error_message(wcroot,
14745 /* We don't allow copies to contain server-excluded nodes;
14746 the update editor is going to have to bail out. */
14747 SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath, scratch_pool));
14749 SVN_WC__DB_WITH_TXN(
14750 make_copy_txn(wcroot, local_relpath,
14751 relpath_depth(local_relpath), conflicts, work_items,
14755 return SVN_NO_ERROR;
14759 svn_wc__db_info_below_working(svn_boolean_t *have_base,
14760 svn_boolean_t *have_work,
14761 svn_wc__db_status_t *status,
14763 const char *local_abspath,
14764 apr_pool_t *scratch_pool)
14766 svn_wc__db_wcroot_t *wcroot;
14767 const char *local_relpath;
14769 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14771 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14772 local_abspath, scratch_pool, scratch_pool));
14773 VERIFY_USABLE_WCROOT(wcroot);
14774 SVN_ERR(info_below_working(have_base, have_work, status,
14775 wcroot, local_relpath, -1, scratch_pool));
14777 return SVN_NO_ERROR;
14781 svn_wc__db_get_not_present_descendants(const apr_array_header_t **descendants,
14783 const char *local_abspath,
14784 apr_pool_t *result_pool,
14785 apr_pool_t *scratch_pool)
14787 svn_wc__db_wcroot_t *wcroot;
14788 const char *local_relpath;
14789 svn_sqlite__stmt_t *stmt;
14790 svn_boolean_t have_row;
14792 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14794 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14795 local_abspath, scratch_pool, scratch_pool));
14796 VERIFY_USABLE_WCROOT(wcroot);
14798 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14799 STMT_SELECT_NOT_PRESENT_DESCENDANTS));
14801 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
14804 relpath_depth(local_relpath)));
14806 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14810 apr_array_header_t *paths;
14812 paths = apr_array_make(result_pool, 4, sizeof(const char*));
14815 const char *found_relpath = svn_sqlite__column_text(stmt, 0, NULL);
14817 APR_ARRAY_PUSH(paths, const char *)
14818 = apr_pstrdup(result_pool, svn_relpath_skip_ancestor(
14819 local_relpath, found_relpath));
14821 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14824 *descendants = paths;
14827 *descendants = apr_array_make(result_pool, 0, sizeof(const char*));
14829 return svn_error_trace(svn_sqlite__reset(stmt));
14833 /* Like svn_wc__db_min_max_revisions(),
14834 * but accepts a WCROOT/LOCAL_RELPATH pair. */
14835 static svn_error_t *
14836 get_min_max_revisions(svn_revnum_t *min_revision,
14837 svn_revnum_t *max_revision,
14838 svn_wc__db_wcroot_t *wcroot,
14839 const char *local_relpath,
14840 svn_boolean_t committed,
14841 apr_pool_t *scratch_pool)
14843 svn_sqlite__stmt_t *stmt;
14844 svn_revnum_t min_rev, max_rev;
14846 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14847 STMT_SELECT_MIN_MAX_REVISIONS));
14848 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14849 SVN_ERR(svn_sqlite__step_row(stmt));
14853 min_rev = svn_sqlite__column_revnum(stmt, 2);
14854 max_rev = svn_sqlite__column_revnum(stmt, 3);
14858 min_rev = svn_sqlite__column_revnum(stmt, 0);
14859 max_rev = svn_sqlite__column_revnum(stmt, 1);
14862 /* The statement returns exactly one row. */
14863 SVN_ERR(svn_sqlite__reset(stmt));
14866 *min_revision = min_rev;
14868 *max_revision = max_rev;
14870 return SVN_NO_ERROR;
14875 svn_wc__db_min_max_revisions(svn_revnum_t *min_revision,
14876 svn_revnum_t *max_revision,
14878 const char *local_abspath,
14879 svn_boolean_t committed,
14880 apr_pool_t *scratch_pool)
14882 svn_wc__db_wcroot_t *wcroot;
14883 const char *local_relpath;
14885 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14887 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14889 scratch_pool, scratch_pool));
14890 VERIFY_USABLE_WCROOT(wcroot);
14892 return svn_error_trace(get_min_max_revisions(min_revision, max_revision,
14893 wcroot, local_relpath,
14894 committed, scratch_pool));
14898 /* Set *IS_SPARSE_CHECKOUT TRUE if LOCAL_RELPATH or any of the nodes
14899 * within LOCAL_RELPATH is sparse, FALSE otherwise. */
14900 static svn_error_t *
14901 is_sparse_checkout_internal(svn_boolean_t *is_sparse_checkout,
14902 svn_wc__db_wcroot_t *wcroot,
14903 const char *local_relpath,
14904 apr_pool_t *scratch_pool)
14906 svn_sqlite__stmt_t *stmt;
14907 svn_boolean_t have_row;
14909 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14910 STMT_HAS_SPARSE_NODES));
14911 SVN_ERR(svn_sqlite__bindf(stmt, "is",
14914 /* If this query returns a row, the working copy is sparse. */
14915 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14916 *is_sparse_checkout = have_row;
14917 SVN_ERR(svn_sqlite__reset(stmt));
14919 return SVN_NO_ERROR;
14923 /* Like svn_wc__db_has_switched_subtrees(),
14924 * but accepts a WCROOT/LOCAL_RELPATH pair. */
14925 static svn_error_t *
14926 has_switched_subtrees(svn_boolean_t *is_switched,
14927 svn_wc__db_wcroot_t *wcroot,
14928 const char *local_relpath,
14929 const char *trail_url,
14930 apr_pool_t *scratch_pool)
14932 svn_sqlite__stmt_t *stmt;
14933 svn_boolean_t have_row;
14934 apr_int64_t repos_id;
14935 const char *repos_relpath;
14937 /* Optional argument handling for caller */
14939 return SVN_NO_ERROR;
14941 *is_switched = FALSE;
14943 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
14944 &repos_relpath, &repos_id,
14945 NULL, NULL, NULL, NULL, NULL,
14946 NULL, NULL, NULL, NULL, NULL,
14947 wcroot, local_relpath,
14948 scratch_pool, scratch_pool));
14950 /* First do the cheap check where we only need info on the origin itself */
14951 if (trail_url != NULL)
14953 const char *repos_root_url;
14955 apr_size_t len1, len2;
14957 /* If the trailing part of the URL of the working copy directory
14958 does not match the given trailing URL then the whole working
14959 copy is switched. */
14961 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot->sdb,
14962 repos_id, scratch_pool));
14963 url = svn_path_url_add_component2(repos_root_url, repos_relpath,
14966 len1 = strlen(trail_url);
14967 len2 = strlen(url);
14968 if ((len1 > len2) || strcmp(url + len2 - len1, trail_url))
14970 *is_switched = TRUE;
14971 return SVN_NO_ERROR;
14975 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_HAS_SWITCHED));
14976 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath, repos_relpath));
14977 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14979 *is_switched = TRUE;
14980 SVN_ERR(svn_sqlite__reset(stmt));
14982 return SVN_NO_ERROR;
14987 svn_wc__db_has_switched_subtrees(svn_boolean_t *is_switched,
14989 const char *local_abspath,
14990 const char *trail_url,
14991 apr_pool_t *scratch_pool)
14993 svn_wc__db_wcroot_t *wcroot;
14994 const char *local_relpath;
14996 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14998 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15000 scratch_pool, scratch_pool));
15001 VERIFY_USABLE_WCROOT(wcroot);
15003 return svn_error_trace(has_switched_subtrees(is_switched, wcroot,
15004 local_relpath, trail_url,
15009 svn_wc__db_get_excluded_subtrees(apr_hash_t **excluded_subtrees,
15011 const char *local_abspath,
15012 apr_pool_t *result_pool,
15013 apr_pool_t *scratch_pool)
15015 svn_wc__db_wcroot_t *wcroot;
15016 const char *local_relpath;
15017 svn_sqlite__stmt_t *stmt;
15018 svn_boolean_t have_row;
15020 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15021 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15023 scratch_pool, scratch_pool));
15024 VERIFY_USABLE_WCROOT(wcroot);
15026 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15027 STMT_SELECT_ALL_EXCLUDED_DESCENDANTS));
15028 SVN_ERR(svn_sqlite__bindf(stmt, "is",
15031 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15034 *excluded_subtrees = apr_hash_make(result_pool);
15036 *excluded_subtrees = NULL;
15040 const char *abs_path =
15041 svn_dirent_join(wcroot->abspath,
15042 svn_sqlite__column_text(stmt, 0, NULL),
15044 svn_hash_sets(*excluded_subtrees, abs_path, abs_path);
15045 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15048 SVN_ERR(svn_sqlite__reset(stmt));
15049 return SVN_NO_ERROR;
15052 /* Like svn_wc__db_has_local_mods(),
15053 * but accepts a WCROOT/LOCAL_RELPATH pair.
15054 * ### This needs a DB as well as a WCROOT/RELPATH pair... */
15055 static svn_error_t *
15056 has_local_mods(svn_boolean_t *is_modified,
15057 svn_wc__db_wcroot_t *wcroot,
15058 const char *local_relpath,
15060 svn_cancel_func_t cancel_func,
15061 void *cancel_baton,
15062 apr_pool_t *scratch_pool)
15064 svn_sqlite__stmt_t *stmt;
15066 /* Check for additions or deletions. */
15067 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15068 STMT_SUBTREE_HAS_TREE_MODIFICATIONS));
15069 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15070 /* If this query returns a row, the working copy is modified. */
15071 SVN_ERR(svn_sqlite__step(is_modified, stmt));
15072 SVN_ERR(svn_sqlite__reset(stmt));
15075 SVN_ERR(cancel_func(cancel_baton));
15077 if (! *is_modified)
15079 /* Check for property modifications. */
15080 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15081 STMT_SUBTREE_HAS_PROP_MODIFICATIONS));
15082 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15083 /* If this query returns a row, the working copy is modified. */
15084 SVN_ERR(svn_sqlite__step(is_modified, stmt));
15085 SVN_ERR(svn_sqlite__reset(stmt));
15088 SVN_ERR(cancel_func(cancel_baton));
15091 if (! *is_modified)
15093 apr_pool_t *iterpool = NULL;
15094 svn_boolean_t have_row;
15096 /* Check for text modifications. */
15097 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15098 STMT_SELECT_BASE_FILES_RECURSIVE));
15099 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15100 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15102 iterpool = svn_pool_create(scratch_pool);
15105 const char *node_abspath;
15106 svn_filesize_t recorded_size;
15107 apr_time_t recorded_time;
15108 svn_boolean_t skip_check = FALSE;
15113 err = cancel_func(cancel_baton);
15115 return svn_error_trace(svn_error_compose_create(
15117 svn_sqlite__reset(stmt)));
15120 svn_pool_clear(iterpool);
15122 node_abspath = svn_dirent_join(wcroot->abspath,
15123 svn_sqlite__column_text(stmt, 0,
15127 recorded_size = get_recorded_size(stmt, 1);
15128 recorded_time = svn_sqlite__column_int64(stmt, 2);
15130 if (recorded_size != SVN_INVALID_FILESIZE
15131 && recorded_time != 0)
15133 const svn_io_dirent2_t *dirent;
15135 err = svn_io_stat_dirent2(&dirent, node_abspath, FALSE, TRUE,
15136 iterpool, iterpool);
15138 return svn_error_trace(svn_error_compose_create(
15140 svn_sqlite__reset(stmt)));
15142 if (dirent->kind != svn_node_file)
15144 *is_modified = TRUE; /* Missing or obstruction */
15147 else if (dirent->filesize == recorded_size
15148 && dirent->mtime == recorded_time)
15150 /* The file is not modified */
15157 err = svn_wc__internal_file_modified_p(is_modified,
15162 return svn_error_trace(svn_error_compose_create(
15164 svn_sqlite__reset(stmt)));
15170 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15173 svn_pool_destroy(iterpool);
15175 SVN_ERR(svn_sqlite__reset(stmt));
15178 return SVN_NO_ERROR;
15183 svn_wc__db_has_local_mods(svn_boolean_t *is_modified,
15185 const char *local_abspath,
15186 svn_cancel_func_t cancel_func,
15187 void *cancel_baton,
15188 apr_pool_t *scratch_pool)
15190 svn_wc__db_wcroot_t *wcroot;
15191 const char *local_relpath;
15193 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15195 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15197 scratch_pool, scratch_pool));
15198 VERIFY_USABLE_WCROOT(wcroot);
15200 return svn_error_trace(has_local_mods(is_modified, wcroot, local_relpath,
15201 db, cancel_func, cancel_baton,
15206 /* The body of svn_wc__db_revision_status().
15208 static svn_error_t *
15209 revision_status_txn(svn_revnum_t *min_revision,
15210 svn_revnum_t *max_revision,
15211 svn_boolean_t *is_sparse_checkout,
15212 svn_boolean_t *is_modified,
15213 svn_boolean_t *is_switched,
15214 svn_wc__db_wcroot_t *wcroot,
15215 const char *local_relpath,
15217 const char *trail_url,
15218 svn_boolean_t committed,
15219 svn_cancel_func_t cancel_func,
15220 void *cancel_baton,
15221 apr_pool_t *scratch_pool)
15224 svn_boolean_t exists;
15226 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
15230 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
15231 _("The node '%s' was not found."),
15232 path_for_error_message(wcroot, local_relpath,
15236 /* Determine mixed-revisionness. */
15237 SVN_ERR(get_min_max_revisions(min_revision, max_revision, wcroot,
15238 local_relpath, committed, scratch_pool));
15241 SVN_ERR(cancel_func(cancel_baton));
15243 /* Determine sparseness. */
15244 SVN_ERR(is_sparse_checkout_internal(is_sparse_checkout, wcroot,
15245 local_relpath, scratch_pool));
15248 SVN_ERR(cancel_func(cancel_baton));
15250 /* Check for switched nodes. */
15252 err = has_switched_subtrees(is_switched, wcroot, local_relpath,
15253 trail_url, scratch_pool);
15257 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
15258 return svn_error_trace(err);
15260 svn_error_clear(err); /* No Base node, but no fatal error */
15261 *is_switched = FALSE;
15266 SVN_ERR(cancel_func(cancel_baton));
15268 /* Check for local mods. */
15269 SVN_ERR(has_local_mods(is_modified, wcroot, local_relpath, db,
15270 cancel_func, cancel_baton, scratch_pool));
15272 return SVN_NO_ERROR;
15277 svn_wc__db_revision_status(svn_revnum_t *min_revision,
15278 svn_revnum_t *max_revision,
15279 svn_boolean_t *is_sparse_checkout,
15280 svn_boolean_t *is_modified,
15281 svn_boolean_t *is_switched,
15283 const char *local_abspath,
15284 const char *trail_url,
15285 svn_boolean_t committed,
15286 svn_cancel_func_t cancel_func,
15287 void *cancel_baton,
15288 apr_pool_t *scratch_pool)
15290 svn_wc__db_wcroot_t *wcroot;
15291 const char *local_relpath;
15293 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15295 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15297 scratch_pool, scratch_pool));
15298 VERIFY_USABLE_WCROOT(wcroot);
15300 SVN_WC__DB_WITH_TXN(
15301 revision_status_txn(min_revision, max_revision,
15302 is_sparse_checkout, is_modified, is_switched,
15303 wcroot, local_relpath, db,
15304 trail_url, committed, cancel_func, cancel_baton,
15307 return SVN_NO_ERROR;
15312 svn_wc__db_base_get_lock_tokens_recursive(apr_hash_t **lock_tokens,
15314 const char *local_abspath,
15315 apr_pool_t *result_pool,
15316 apr_pool_t *scratch_pool)
15318 svn_wc__db_wcroot_t *wcroot;
15319 const char *local_relpath;
15320 svn_sqlite__stmt_t *stmt;
15321 svn_boolean_t have_row;
15322 apr_int64_t last_repos_id = INVALID_REPOS_ID;
15323 const char *last_repos_root_url = NULL;
15325 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15327 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15329 scratch_pool, scratch_pool));
15330 VERIFY_USABLE_WCROOT(wcroot);
15332 *lock_tokens = apr_hash_make(result_pool);
15334 /* Fetch all the lock tokens in and under LOCAL_RELPATH. */
15335 SVN_ERR(svn_sqlite__get_statement(
15336 &stmt, wcroot->sdb,
15337 STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE));
15339 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15340 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15343 apr_int64_t child_repos_id = svn_sqlite__column_int64(stmt, 0);
15344 const char *child_relpath = svn_sqlite__column_text(stmt, 1, NULL);
15345 const char *lock_token = svn_sqlite__column_text(stmt, 2, result_pool);
15347 if (child_repos_id != last_repos_id)
15349 svn_error_t *err = svn_wc__db_fetch_repos_info(&last_repos_root_url,
15356 return svn_error_trace(
15357 svn_error_compose_create(err,
15358 svn_sqlite__reset(stmt)));
15361 last_repos_id = child_repos_id;
15364 SVN_ERR_ASSERT(last_repos_root_url != NULL);
15365 svn_hash_sets(*lock_tokens,
15366 svn_path_url_add_component2(last_repos_root_url,
15367 child_relpath, result_pool),
15370 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15372 return svn_sqlite__reset(stmt);
15376 /* If EXPRESSION is false, cause the caller to return an SVN_ERR_WC_CORRUPT
15377 * error, showing EXPRESSION and the caller's LOCAL_RELPATH in the message. */
15378 #define VERIFY(expression) \
15380 if (! (expression)) \
15381 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, \
15382 _("database inconsistency at local_relpath='%s' verifying " \
15383 "expression '%s'"), local_relpath, #expression); \
15387 /* Verify consistency of the metadata concerning WCROOT. This is intended
15388 * for use only during testing and debugging, so is not intended to be
15391 * This code is a complement to any verification that we can do in SQLite
15392 * triggers. See, for example, 'wc-checks.sql'.
15394 * Some more verification steps we might want to add are:
15396 * * on every ACTUAL row (except root): a NODES row exists at its parent path
15397 * * the op-depth root must always exist and every intermediate too
15399 static svn_error_t *
15400 verify_wcroot(svn_wc__db_wcroot_t *wcroot,
15401 apr_pool_t *scratch_pool)
15403 svn_sqlite__stmt_t *stmt;
15404 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15406 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15407 STMT_SELECT_ALL_NODES));
15408 SVN_ERR(svn_sqlite__bindf(stmt, "i", wcroot->wc_id));
15411 svn_boolean_t have_row;
15412 const char *local_relpath, *parent_relpath;
15415 svn_pool_clear(iterpool);
15417 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15421 op_depth = svn_sqlite__column_int(stmt, 0);
15422 local_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
15423 parent_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
15425 /* Verify parent_relpath is the parent path of local_relpath */
15426 VERIFY((parent_relpath == NULL)
15427 ? (local_relpath[0] == '\0')
15428 : (strcmp(svn_relpath_dirname(local_relpath, iterpool),
15429 parent_relpath) == 0));
15431 /* Verify op_depth <= the tree depth of local_relpath */
15432 VERIFY(op_depth <= relpath_depth(local_relpath));
15434 /* Verify parent_relpath refers to a row that exists */
15435 /* TODO: Verify there is a suitable parent row - e.g. has op_depth <=
15436 * the child's and a suitable presence */
15437 if (parent_relpath && svn_sqlite__column_is_null(stmt, 3))
15439 svn_sqlite__stmt_t *stmt2;
15440 svn_boolean_t have_a_parent_row;
15442 SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
15443 STMT_SELECT_NODE_INFO));
15444 SVN_ERR(svn_sqlite__bindf(stmt2, "is", wcroot->wc_id,
15446 SVN_ERR(svn_sqlite__step(&have_a_parent_row, stmt2));
15447 VERIFY(have_a_parent_row);
15448 SVN_ERR(svn_sqlite__reset(stmt2));
15451 svn_pool_destroy(iterpool);
15453 return svn_error_trace(svn_sqlite__reset(stmt));
15457 svn_wc__db_verify(svn_wc__db_t *db,
15458 const char *wri_abspath,
15459 apr_pool_t *scratch_pool)
15461 svn_wc__db_wcroot_t *wcroot;
15462 const char *local_relpath;
15464 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15466 scratch_pool, scratch_pool));
15467 VERIFY_USABLE_WCROOT(wcroot);
15469 SVN_ERR(verify_wcroot(wcroot, scratch_pool));
15470 return SVN_NO_ERROR;
15474 svn_wc__db_bump_format(int *result_format,
15475 svn_boolean_t *bumped_format,
15477 const char *wcroot_abspath,
15478 apr_pool_t *scratch_pool)
15480 svn_sqlite__db_t *sdb;
15485 *bumped_format = FALSE;
15487 /* Do not scan upwards for a working copy root here to prevent accidental
15488 * upgrades of any working copies the WCROOT might be nested in.
15489 * Just try to open a DB at the specified path instead. */
15490 err = svn_wc__db_util_open_db(&sdb, wcroot_abspath, SDB_FILE,
15491 svn_sqlite__mode_readwrite,
15492 TRUE, /* exclusive */
15493 NULL, /* my statements */
15494 scratch_pool, scratch_pool);
15498 apr_hash_t *entries;
15500 /* Could not open an sdb. Check for an entries file instead. */
15501 err2 = svn_wc__read_entries_old(&entries, wcroot_abspath,
15502 scratch_pool, scratch_pool);
15503 if (err2 || apr_hash_count(entries) == 0)
15504 return svn_error_createf(SVN_ERR_WC_INVALID_OP_ON_CWD,
15505 svn_error_compose_create(err, err2),
15506 _("Can't upgrade '%s' as it is not a working copy root"),
15507 svn_dirent_local_style(wcroot_abspath, scratch_pool));
15509 /* An entries file was found. This is a pre-wc-ng working copy
15510 * so suggest an upgrade. */
15511 return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, err,
15512 _("Working copy '%s' is too old and must be upgraded to "
15513 "at least format %d, as created by Subversion %s"),
15514 svn_dirent_local_style(wcroot_abspath, scratch_pool),
15515 SVN_WC__WC_NG_VERSION,
15516 svn_wc__version_string_from_format(SVN_WC__WC_NG_VERSION));
15519 SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool));
15520 err = svn_wc__upgrade_sdb(result_format, wcroot_abspath,
15521 sdb, format, scratch_pool);
15523 if (err == SVN_NO_ERROR && bumped_format)
15524 *bumped_format = (*result_format > format);
15526 /* Make sure we return a different error than expected for upgrades from
15528 if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
15529 err = svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, err,
15530 _("Working copy upgrade failed"));
15532 err = svn_error_compose_create(err, svn_sqlite__close(sdb));
15534 return svn_error_trace(err);
15538 svn_wc__db_vacuum(svn_wc__db_t *db,
15539 const char *local_abspath,
15540 apr_pool_t *scratch_pool)
15542 svn_wc__db_wcroot_t *wcroot;
15543 const char *local_relpath;
15545 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15547 scratch_pool, scratch_pool));
15548 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_VACUUM));
15550 return SVN_NO_ERROR;