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_private_config.h"
31 #include "svn_types.h"
32 #include "svn_error.h"
33 #include "svn_dirent_uri.h"
36 #include "svn_sorts.h"
38 #include "svn_checksum.h"
39 #include "svn_pools.h"
43 #include "adm_files.h"
44 #include "wc-queries.h"
47 #include "conflicts.h"
48 #include "wc_db_private.h"
49 #include "workqueue.h"
50 #include "token-map.h"
52 #include "private/svn_sorts_private.h"
53 #include "private/svn_sqlite.h"
54 #include "private/svn_skel.h"
55 #include "private/svn_wc_private.h"
56 #include "private/svn_token.h"
59 #define NOT_IMPLEMENTED() SVN__NOT_IMPLEMENTED()
63 * Some filename constants.
65 #define SDB_FILE "wc.db"
67 #define WCROOT_TEMPDIR_RELPATH "tmp"
71 * PARAMETER ASSERTIONS
73 * Every (semi-)public entrypoint in this file has a set of assertions on
74 * the parameters passed into the function. Since this is a brand new API,
75 * we want to make sure that everybody calls it properly. The original WC
76 * code had years to catch stray bugs, but we do not have that luxury in
77 * the wc-nb rewrite. Any extra assurances that we can find will be
78 * welcome. The asserts will ensure we have no doubt about the values
79 * passed into the function.
81 * Some parameters are *not* specifically asserted. Typically, these are
82 * params that will be used immediately, so something like a NULL value
85 * ### near 1.7 release, it would be a Good Thing to review the assertions
86 * ### and decide if any can be removed or switched to assert() in order
87 * ### to remove their runtime cost in the production release.
92 * Each function should leave the database in a consistent state. If it
93 * does *not*, then the implication is some other function needs to be
94 * called to restore consistency. Subtle requirements like that are hard
95 * to maintain over a long period of time, so this API will not allow it.
98 * STANDARD VARIABLE NAMES
100 * db working copy database (this module)
101 * sdb SQLite database (not to be confused with 'db')
102 * wc_id a WCROOT id associated with a node
105 #define INVALID_REPOS_ID ((apr_int64_t) -1)
106 #define UNKNOWN_WC_ID ((apr_int64_t) -1)
107 #define FORMAT_FROM_SDB (-1)
109 /* Check if column number I, a property-skel column, contains a non-empty
110 set of properties. The empty set of properties is stored as "()", so we
111 have properties if the size of the column is larger than 2. */
112 #define SQLITE_PROPERTIES_AVAILABLE(stmt, i) \
113 (svn_sqlite__column_bytes(stmt, i) > 2)
116 svn_wc__db_op_depth_for_upgrade(const char *local_relpath)
118 return relpath_depth(local_relpath);
122 /* Representation of a new base row for the NODES table */
123 typedef struct insert_base_baton_t {
124 /* common to all insertions into BASE */
125 svn_wc__db_status_t status;
126 svn_node_kind_t kind;
127 apr_int64_t repos_id;
128 const char *repos_relpath;
129 svn_revnum_t revision;
131 /* Only used when repos_id == INVALID_REPOS_ID */
132 const char *repos_root_url;
133 const char *repos_uuid;
135 /* common to all "normal" presence insertions */
136 const apr_hash_t *props;
137 svn_revnum_t changed_rev;
138 apr_time_t changed_date;
139 const char *changed_author;
140 const apr_hash_t *dav_cache;
142 /* for inserting directories */
143 const apr_array_header_t *children;
146 /* for inserting files */
147 const svn_checksum_t *checksum;
149 /* for inserting symlinks */
152 svn_boolean_t file_external;
154 /* may need to insert/update ACTUAL to record a conflict */
155 const svn_skel_t *conflict;
157 /* may need to insert/update ACTUAL to record new properties */
158 svn_boolean_t update_actual_props;
159 const apr_hash_t *new_actual_props;
161 /* A depth-first ordered array of svn_prop_inherited_item_t *
162 structures representing the properties inherited by the base
164 apr_array_header_t *iprops;
166 /* maybe we should copy information from a previous record? */
167 svn_boolean_t keep_recorded_info;
169 /* insert a base-deleted working node as well as a base node */
170 svn_boolean_t insert_base_deleted;
172 /* delete the current working nodes above BASE */
173 svn_boolean_t delete_working;
175 /* may have work items to queue in this transaction */
176 const svn_skel_t *work_items;
178 } insert_base_baton_t;
181 /* Representation of a new working row for the NODES table */
182 typedef struct insert_working_baton_t {
183 /* common to all insertions into WORKING (including NODE_DATA) */
184 svn_wc__db_status_t presence;
185 svn_node_kind_t kind;
188 /* common to all "normal" presence insertions */
189 const apr_hash_t *props;
190 svn_revnum_t changed_rev;
191 apr_time_t changed_date;
192 const char *changed_author;
193 apr_int64_t original_repos_id;
194 const char *original_repos_relpath;
195 svn_revnum_t original_revnum;
196 svn_boolean_t moved_here;
198 /* for inserting directories */
199 const apr_array_header_t *children;
202 /* for inserting (copied/moved-here) files */
203 const svn_checksum_t *checksum;
205 /* for inserting symlinks */
208 svn_boolean_t update_actual_props;
209 const apr_hash_t *new_actual_props;
211 /* may have work items to queue in this transaction */
212 const svn_skel_t *work_items;
214 /* may have conflict to install in this transaction */
215 const svn_skel_t *conflict;
217 /* If the value is > 0 and < op_depth, also insert a not-present
218 at op-depth NOT_PRESENT_OP_DEPTH, based on this same information */
219 int not_present_op_depth;
221 } insert_working_baton_t;
223 /* Representation of a new row for the EXTERNALS table */
224 typedef struct insert_external_baton_t {
225 /* common to all insertions into EXTERNALS */
226 svn_node_kind_t kind;
227 svn_wc__db_status_t presence;
229 /* The repository of the external */
230 apr_int64_t repos_id;
231 /* for file and symlink externals */
232 const char *repos_relpath;
233 svn_revnum_t revision;
235 /* Only used when repos_id == INVALID_REPOS_ID */
236 const char *repos_root_url;
237 const char *repos_uuid;
239 /* for file and symlink externals */
240 const apr_hash_t *props;
241 apr_array_header_t *iprops;
242 svn_revnum_t changed_rev;
243 apr_time_t changed_date;
244 const char *changed_author;
245 const apr_hash_t *dav_cache;
247 /* for inserting files */
248 const svn_checksum_t *checksum;
250 /* for inserting symlinks */
253 const char *record_ancestor_relpath;
254 const char *recorded_repos_relpath;
255 svn_revnum_t recorded_peg_revision;
256 svn_revnum_t recorded_revision;
258 /* may need to insert/update ACTUAL to record a conflict */
259 const svn_skel_t *conflict;
261 /* may need to insert/update ACTUAL to record new properties */
262 svn_boolean_t update_actual_props;
263 const apr_hash_t *new_actual_props;
265 /* maybe we should copy information from a previous record? */
266 svn_boolean_t keep_recorded_info;
268 /* may have work items to queue in this transaction */
269 const svn_skel_t *work_items;
271 } insert_external_baton_t;
274 /* Forward declarations */
276 add_work_items(svn_sqlite__db_t *sdb,
277 const svn_skel_t *skel,
278 apr_pool_t *scratch_pool);
281 set_actual_props(svn_wc__db_wcroot_t *wcroot,
282 const char *local_relpath,
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 db_is_switched(svn_boolean_t *is_switched,
358 svn_node_kind_t *kind,
359 svn_wc__db_wcroot_t *wcroot,
360 const char *local_relpath,
361 apr_pool_t *scratch_pool);
364 /* Return the absolute path, in local path style, of LOCAL_RELPATH
367 path_for_error_message(const svn_wc__db_wcroot_t *wcroot,
368 const char *local_relpath,
369 apr_pool_t *result_pool)
371 const char *local_abspath
372 = svn_dirent_join(wcroot->abspath, local_relpath, result_pool);
374 return svn_dirent_local_style(local_abspath, result_pool);
378 /* Return a file size from column SLOT of the SQLITE statement STMT, or
379 SVN_INVALID_FILESIZE if the column value is NULL. */
380 static svn_filesize_t
381 get_recorded_size(svn_sqlite__stmt_t *stmt, int slot)
383 if (svn_sqlite__column_is_null(stmt, slot))
384 return SVN_INVALID_FILESIZE;
385 return svn_sqlite__column_int64(stmt, slot);
389 /* Return a lock info structure constructed from the given columns of the
390 SQLITE statement STMT, or return NULL if the token column value is null. */
391 static svn_wc__db_lock_t *
392 lock_from_columns(svn_sqlite__stmt_t *stmt,
397 apr_pool_t *result_pool)
399 svn_wc__db_lock_t *lock;
401 if (svn_sqlite__column_is_null(stmt, col_token))
407 lock = apr_pcalloc(result_pool, sizeof(svn_wc__db_lock_t));
408 lock->token = svn_sqlite__column_text(stmt, col_token, result_pool);
409 lock->owner = svn_sqlite__column_text(stmt, col_owner, result_pool);
410 lock->comment = svn_sqlite__column_text(stmt, col_comment, result_pool);
411 lock->date = svn_sqlite__column_int64(stmt, col_date);
418 svn_wc__db_fetch_repos_info(const char **repos_root_url,
419 const char **repos_uuid,
420 svn_wc__db_wcroot_t *wcroot,
421 apr_int64_t repos_id,
422 apr_pool_t *result_pool)
424 svn_sqlite__stmt_t *stmt;
425 svn_boolean_t have_row;
427 if (!repos_root_url && !repos_uuid)
430 if (repos_id == INVALID_REPOS_ID)
433 *repos_root_url = NULL;
439 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
440 STMT_SELECT_REPOSITORY_BY_ID));
441 SVN_ERR(svn_sqlite__bindf(stmt, "i", repos_id));
442 SVN_ERR(svn_sqlite__step(&have_row, stmt));
444 return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
445 _("No REPOSITORY table entry for id '%ld'"),
449 *repos_root_url = svn_sqlite__column_text(stmt, 0, result_pool);
451 *repos_uuid = svn_sqlite__column_text(stmt, 1, result_pool);
453 return svn_error_trace(svn_sqlite__reset(stmt));
456 /* Set *REPOS_ID, *REVISION and *REPOS_RELPATH from the given columns of the
457 SQLITE statement STMT, or to NULL/SVN_INVALID_REVNUM if the respective
458 column value is null. Any of the output parameters may be NULL if not
461 repos_location_from_columns(apr_int64_t *repos_id,
462 svn_revnum_t *revision,
463 const char **repos_relpath,
464 svn_sqlite__stmt_t *stmt,
467 int col_repos_relpath,
468 apr_pool_t *result_pool)
472 /* Fetch repository information via REPOS_ID. */
473 if (svn_sqlite__column_is_null(stmt, col_repos_id))
474 *repos_id = INVALID_REPOS_ID;
476 *repos_id = svn_sqlite__column_int64(stmt, col_repos_id);
480 *revision = svn_sqlite__column_revnum(stmt, col_revision);
484 *repos_relpath = svn_sqlite__column_text(stmt, col_repos_relpath,
489 /* For a given REPOS_ROOT_URL/REPOS_UUID pair, return the existing REPOS_ID
490 value. If one does not exist, then create a new one. */
492 create_repos_id(apr_int64_t *repos_id,
493 const char *repos_root_url,
494 const char *repos_uuid,
495 svn_sqlite__db_t *sdb,
496 apr_pool_t *scratch_pool)
498 svn_sqlite__stmt_t *get_stmt;
499 svn_sqlite__stmt_t *insert_stmt;
500 svn_boolean_t have_row;
502 SVN_ERR(svn_sqlite__get_statement(&get_stmt, sdb, STMT_SELECT_REPOSITORY));
503 SVN_ERR(svn_sqlite__bindf(get_stmt, "s", repos_root_url));
504 SVN_ERR(svn_sqlite__step(&have_row, get_stmt));
508 *repos_id = svn_sqlite__column_int64(get_stmt, 0);
509 return svn_error_trace(svn_sqlite__reset(get_stmt));
511 SVN_ERR(svn_sqlite__reset(get_stmt));
513 /* NOTE: strictly speaking, there is a race condition between the
514 above query and the insertion below. We're simply going to ignore
515 that, as it means two processes are *modifying* the working copy
516 at the same time, *and* new repositores are becoming visible.
517 This is rare enough, let alone the miniscule chance of hitting
518 this race condition. Further, simply failing out will leave the
519 database in a consistent state, and the user can just re-run the
522 SVN_ERR(svn_sqlite__get_statement(&insert_stmt, sdb,
523 STMT_INSERT_REPOSITORY));
524 SVN_ERR(svn_sqlite__bindf(insert_stmt, "ss", repos_root_url, repos_uuid));
525 return svn_error_trace(svn_sqlite__insert(repos_id, insert_stmt));
529 /* Initialize the baton with appropriate "blank" values. This allows the
530 insertion function to leave certain columns null. */
532 blank_ibb(insert_base_baton_t *pibb)
534 memset(pibb, 0, sizeof(*pibb));
535 pibb->revision = SVN_INVALID_REVNUM;
536 pibb->changed_rev = SVN_INVALID_REVNUM;
537 pibb->depth = svn_depth_infinity;
538 pibb->repos_id = INVALID_REPOS_ID;
542 /* Extend any delete of the parent of LOCAL_RELPATH to LOCAL_RELPATH.
544 ### What about KIND and OP_DEPTH? KIND ought to be redundant; I'm
545 discussing on dev@ whether we can let that be null for presence
546 == base-deleted. OP_DEPTH is the op-depth of what, and why?
547 It is used to select the lowest working node higher than OP_DEPTH,
548 so, in terms of the API, OP_DEPTH means ...?
556 A/B/C not-pres normal
559 That is checkout, delete A/B, copy a replacement A/B, delete copied
560 child A/B/C, add replacement A/B/C, add A/B/C/D.
562 Now an update that adds base nodes for A/B/C, A/B/C/D and A/B/C/D/E
563 must extend the A/B deletion:
569 A/B/C normal not-pres normal
570 A/B/C/D normal base-del normal
571 A/B/C/D/E normal base-del
573 When adding a node if the parent has a higher working node then the
574 parent node is deleted (or replaced) and the delete must be extended
577 In the example above A/B/C/D and A/B/C/D/E are the nodes that get
578 the extended delete, A/B/C is already deleted.
580 If ADDED_DELETE is not NULL, set *ADDED_DELETE to TRUE if a new delete
581 was recorded, otherwise to FALSE.
584 db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot,
585 const char *local_relpath,
586 svn_node_kind_t kind,
588 apr_pool_t *scratch_pool)
590 svn_boolean_t have_row;
591 svn_sqlite__stmt_t *stmt;
593 const char *parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
595 SVN_ERR_ASSERT(local_relpath[0]);
597 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
598 STMT_SELECT_LOWEST_WORKING_NODE));
599 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, parent_relpath,
601 SVN_ERR(svn_sqlite__step(&have_row, stmt));
603 parent_op_depth = svn_sqlite__column_int(stmt, 0);
604 SVN_ERR(svn_sqlite__reset(stmt));
607 int existing_op_depth;
609 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
611 SVN_ERR(svn_sqlite__step(&have_row, stmt));
613 existing_op_depth = svn_sqlite__column_int(stmt, 0);
614 SVN_ERR(svn_sqlite__reset(stmt));
615 if (!have_row || parent_op_depth < existing_op_depth)
617 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
618 STMT_INSTALL_WORKING_NODE_FOR_DELETE));
619 SVN_ERR(svn_sqlite__bindf(stmt, "isdst", wcroot->wc_id,
620 local_relpath, parent_op_depth,
621 parent_relpath, kind_map, kind));
622 SVN_ERR(svn_sqlite__update(NULL, stmt));
630 /* This is the reverse of db_extend_parent_delete.
632 When removing a node if the parent has a higher working node then
633 the parent node and this node are both deleted or replaced and any
634 delete over this node must be removed.
636 This function (like most wcroot functions) assumes that its caller
637 only uses this function within an sqlite transaction if atomic
641 db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot,
642 const char *local_relpath,
644 apr_pool_t *scratch_pool)
646 svn_sqlite__stmt_t *stmt;
647 svn_boolean_t have_row;
649 svn_wc__db_status_t presence;
650 const char *moved_to;
652 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
653 STMT_SELECT_LOWEST_WORKING_NODE));
654 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
656 SVN_ERR(svn_sqlite__step(&have_row, stmt));
659 return svn_error_trace(svn_sqlite__reset(stmt));
661 working_depth = svn_sqlite__column_int(stmt, 0);
662 presence = svn_sqlite__column_token(stmt, 1, presence_map);
663 moved_to = svn_sqlite__column_text(stmt, 3, scratch_pool);
665 SVN_ERR(svn_sqlite__reset(stmt));
669 /* Turn the move into a copy to keep the NODES table valid */
670 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
671 STMT_CLEAR_MOVED_HERE_RECURSIVE));
672 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
673 moved_to, relpath_depth(moved_to)));
674 SVN_ERR(svn_sqlite__step_done(stmt));
676 /* This leaves just the moved_to information on the origin,
677 which we will remove in the next step */
680 if (presence == svn_wc__db_status_base_deleted)
682 /* Nothing left to shadow; remove the base-deleted node */
683 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_NODE));
687 /* Clear moved to information, as this node is no longer base-deleted */
688 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
689 STMT_CLEAR_MOVED_TO_RELPATH));
693 /* Nothing to update */
697 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
700 return svn_error_trace(svn_sqlite__update(NULL, stmt));
705 /* Insert the base row represented by (insert_base_baton_t *) BATON. */
707 insert_base_node(const insert_base_baton_t *pibb,
708 svn_wc__db_wcroot_t *wcroot,
709 const char *local_relpath,
710 apr_pool_t *scratch_pool)
712 apr_int64_t repos_id = pibb->repos_id;
713 svn_sqlite__stmt_t *stmt;
714 svn_filesize_t recorded_size = SVN_INVALID_FILESIZE;
715 apr_int64_t recorded_time;
716 svn_boolean_t present;
718 /* The directory at the WCROOT has a NULL parent_relpath. Otherwise,
719 bind the appropriate parent_relpath. */
720 const char *parent_relpath =
721 (*local_relpath == '\0') ? NULL
722 : svn_relpath_dirname(local_relpath, scratch_pool);
724 if (pibb->repos_id == INVALID_REPOS_ID)
725 SVN_ERR(create_repos_id(&repos_id, pibb->repos_root_url, pibb->repos_uuid,
726 wcroot->sdb, scratch_pool));
728 SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
729 SVN_ERR_ASSERT(pibb->repos_relpath != NULL);
731 if (pibb->keep_recorded_info)
733 svn_boolean_t have_row;
734 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
735 STMT_SELECT_BASE_NODE));
736 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
737 SVN_ERR(svn_sqlite__step(&have_row, stmt));
740 /* Preserve size and modification time if caller asked us to. */
741 recorded_size = get_recorded_size(stmt, 6);
742 recorded_time = svn_sqlite__column_int64(stmt, 12);
744 SVN_ERR(svn_sqlite__reset(stmt));
747 present = (pibb->status == svn_wc__db_status_normal
748 || pibb->status == svn_wc__db_status_incomplete);
750 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
751 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisr"
753 "isnnnnns", /* 12 - 19 */
754 wcroot->wc_id, /* 1 */
755 local_relpath, /* 2 */
756 0, /* op_depth is 0 for base */
757 parent_relpath, /* 4 */
761 presence_map, pibb->status, /* 8 */
762 (pibb->kind == svn_node_dir && present) /* 9 */
763 ? svn_token__to_word(depth_map, pibb->depth)
765 kind_map, pibb->kind, /* 10 */
766 pibb->changed_rev, /* 11 */
767 pibb->changed_date, /* 12 */
768 pibb->changed_author, /* 13 */
769 (pibb->kind == svn_node_symlink && present) ?
770 pibb->target : NULL)); /* 19 */
771 if (pibb->kind == svn_node_file && present)
774 && pibb->status != svn_wc__db_status_not_present
775 && pibb->status != svn_wc__db_status_excluded
776 && pibb->status != svn_wc__db_status_server_excluded)
777 return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
778 _("The file '%s' has no checksum."),
779 path_for_error_message(wcroot, local_relpath,
782 SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, pibb->checksum,
785 if (recorded_size != SVN_INVALID_FILESIZE)
787 SVN_ERR(svn_sqlite__bind_int64(stmt, 16, recorded_size));
788 SVN_ERR(svn_sqlite__bind_int64(stmt, 17, recorded_time));
792 /* Set properties. Must be null if presence not normal or incomplete. */
793 assert(pibb->status == svn_wc__db_status_normal
794 || pibb->status == svn_wc__db_status_incomplete
795 || pibb->props == NULL);
798 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, pibb->props,
801 SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, pibb->iprops,
806 SVN_ERR(svn_sqlite__bind_properties(stmt, 18, pibb->dav_cache,
809 if (pibb->file_external)
810 SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
812 SVN_ERR(svn_sqlite__insert(NULL, stmt));
814 if (pibb->update_actual_props)
816 /* Cast away const, to allow calling property helpers */
817 apr_hash_t *base_props = (apr_hash_t *)pibb->props;
818 apr_hash_t *new_actual_props = (apr_hash_t *)pibb->new_actual_props;
820 if (base_props != NULL
821 && new_actual_props != NULL
822 && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
824 apr_array_header_t *diffs;
826 SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
829 if (diffs->nelts == 0)
830 new_actual_props = NULL;
833 SVN_ERR(set_actual_props(wcroot, local_relpath, new_actual_props,
837 if (pibb->kind == svn_node_dir && pibb->children)
838 SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
847 /* When this is not the root node, check shadowing behavior */
851 && ((pibb->status == svn_wc__db_status_normal)
852 || (pibb->status == svn_wc__db_status_incomplete))
853 && ! pibb->file_external)
855 SVN_ERR(db_extend_parent_delete(wcroot, local_relpath,
859 else if (pibb->status == svn_wc__db_status_not_present
860 || pibb->status == svn_wc__db_status_server_excluded
861 || pibb->status == svn_wc__db_status_excluded)
863 SVN_ERR(db_retract_parent_delete(wcroot, local_relpath, 0,
868 if (pibb->delete_working)
870 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
871 STMT_DELETE_WORKING_NODE));
872 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
873 SVN_ERR(svn_sqlite__step_done(stmt));
875 if (pibb->insert_base_deleted)
877 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
878 STMT_INSERT_DELETE_FROM_BASE));
879 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
880 wcroot->wc_id, local_relpath,
881 relpath_depth(local_relpath)));
882 SVN_ERR(svn_sqlite__step_done(stmt));
885 SVN_ERR(add_work_items(wcroot->sdb, pibb->work_items, scratch_pool));
887 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
888 pibb->conflict, scratch_pool));
894 /* Initialize the baton with appropriate "blank" values. This allows the
895 insertion function to leave certain columns null. */
897 blank_iwb(insert_working_baton_t *piwb)
899 memset(piwb, 0, sizeof(*piwb));
900 piwb->changed_rev = SVN_INVALID_REVNUM;
901 piwb->depth = svn_depth_infinity;
903 /* ORIGINAL_REPOS_ID and ORIGINAL_REVNUM could use some kind of "nil"
904 value, but... meh. We'll avoid them if ORIGINAL_REPOS_RELPATH==NULL. */
908 /* Insert a row in NODES for each (const char *) child name in CHILDREN,
909 whose parent directory is LOCAL_RELPATH, at op_depth=OP_DEPTH. Set each
910 child's presence to 'incomplete', kind to 'unknown', repos_id to REPOS_ID,
911 repos_path by appending the child name to REPOS_PATH, and revision to
912 REVISION (which should match the parent's revision).
914 If REPOS_ID is INVALID_REPOS_ID, set each child's repos_id to null. */
916 insert_incomplete_children(svn_sqlite__db_t *sdb,
918 const char *local_relpath,
919 apr_int64_t repos_id,
920 const char *repos_path,
921 svn_revnum_t revision,
922 const apr_array_header_t *children,
924 apr_pool_t *scratch_pool)
926 svn_sqlite__stmt_t *stmt;
928 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
929 apr_hash_t *moved_to_relpaths = apr_hash_make(scratch_pool);
931 SVN_ERR_ASSERT(repos_path != NULL || op_depth > 0);
932 SVN_ERR_ASSERT((repos_id != INVALID_REPOS_ID)
933 == (repos_path != NULL));
935 /* If we're inserting WORKING nodes, we might be replacing existing
936 * nodes which were moved-away. We need to retain the moved-to relpath of
937 * such nodes in order not to lose move information during replace. */
940 for (i = children->nelts; i--; )
942 const char *name = APR_ARRAY_IDX(children, i, const char *);
943 svn_boolean_t have_row;
945 svn_pool_clear(iterpool);
947 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
948 STMT_SELECT_WORKING_NODE));
949 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id,
950 svn_relpath_join(local_relpath, name,
952 SVN_ERR(svn_sqlite__step(&have_row, stmt));
953 if (have_row && !svn_sqlite__column_is_null(stmt, 14))
954 svn_hash_sets(moved_to_relpaths, name,
955 svn_sqlite__column_text(stmt, 14, scratch_pool));
957 SVN_ERR(svn_sqlite__reset(stmt));
961 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
963 for (i = children->nelts; i--; )
965 const char *name = APR_ARRAY_IDX(children, i, const char *);
967 svn_pool_clear(iterpool);
969 SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnrsnsnnnnnnnnnnsn",
971 svn_relpath_join(local_relpath, name,
976 "incomplete", /* 8, presence */
977 "unknown", /* 10, kind */
979 svn_hash_gets(moved_to_relpaths, name)));
980 if (repos_id != INVALID_REPOS_ID)
982 SVN_ERR(svn_sqlite__bind_int64(stmt, 5, repos_id));
983 SVN_ERR(svn_sqlite__bind_text(stmt, 6,
984 svn_relpath_join(repos_path, name,
988 SVN_ERR(svn_sqlite__insert(NULL, stmt));
991 svn_pool_destroy(iterpool);
997 /* Insert the working row represented by (insert_working_baton_t *) BATON. */
999 insert_working_node(const insert_working_baton_t *piwb,
1000 svn_wc__db_wcroot_t *wcroot,
1001 const char *local_relpath,
1002 apr_pool_t *scratch_pool)
1004 const char *parent_relpath;
1005 const char *moved_to_relpath = NULL;
1006 svn_sqlite__stmt_t *stmt;
1007 svn_boolean_t have_row;
1008 svn_boolean_t present;
1010 SVN_ERR_ASSERT(piwb->op_depth > 0);
1012 /* We cannot insert a WORKING_NODE row at the wcroot. */
1013 SVN_ERR_ASSERT(*local_relpath != '\0');
1014 parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
1016 /* Preserve existing moved-to information for this relpath,
1017 * which might exist in case we're replacing an existing base-deleted
1019 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
1020 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1022 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1024 moved_to_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
1025 SVN_ERR(svn_sqlite__reset(stmt));
1027 present = (piwb->presence == svn_wc__db_status_normal
1028 || piwb->presence == svn_wc__db_status_incomplete);
1030 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
1031 SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnntstrisn"
1032 "nnnn" /* properties translated_size last_mod_time dav_cache */
1033 "sns", /* symlink_target, file_external, moved_to */
1034 wcroot->wc_id, local_relpath,
1037 presence_map, piwb->presence,
1038 (piwb->kind == svn_node_dir && present)
1039 ? svn_token__to_word(depth_map, piwb->depth) : NULL,
1040 kind_map, piwb->kind,
1043 piwb->changed_author,
1044 /* Note: incomplete nodes may have a NULL target. */
1045 (piwb->kind == svn_node_symlink && present)
1046 ? piwb->target : NULL,
1049 if (piwb->moved_here)
1051 SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
1054 if (piwb->kind == svn_node_file && present)
1056 SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, piwb->checksum,
1060 if (piwb->original_repos_relpath != NULL)
1062 SVN_ERR(svn_sqlite__bind_int64(stmt, 5, piwb->original_repos_id));
1063 SVN_ERR(svn_sqlite__bind_text(stmt, 6, piwb->original_repos_relpath));
1064 SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, piwb->original_revnum));
1067 /* Set properties. Must be null if presence not normal or incomplete. */
1068 assert(piwb->presence == svn_wc__db_status_normal
1069 || piwb->presence == svn_wc__db_status_incomplete
1070 || piwb->props == NULL);
1071 if (present && piwb->original_repos_relpath)
1072 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, piwb->props, scratch_pool));
1074 SVN_ERR(svn_sqlite__insert(NULL, stmt));
1076 /* Insert incomplete children, if specified.
1077 The children are part of the same op and so have the same op_depth.
1078 (The only time we'd want a different depth is during a recursive
1079 simple add, but we never insert children here during a simple add.) */
1080 if (piwb->kind == svn_node_dir && piwb->children)
1081 SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
1083 INVALID_REPOS_ID /* inherit repos_id */,
1084 NULL /* inherit repos_path */,
1085 piwb->original_revnum,
1090 if (piwb->update_actual_props)
1092 /* Cast away const, to allow calling property helpers */
1093 apr_hash_t *base_props = (apr_hash_t *)piwb->props;
1094 apr_hash_t *new_actual_props = (apr_hash_t *)piwb->new_actual_props;
1096 if (base_props != NULL
1097 && new_actual_props != NULL
1098 && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
1100 apr_array_header_t *diffs;
1102 SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
1105 if (diffs->nelts == 0)
1106 new_actual_props = NULL;
1109 SVN_ERR(set_actual_props(wcroot, local_relpath, new_actual_props,
1113 if (piwb->kind == svn_node_dir)
1115 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1116 STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST));
1117 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1118 SVN_ERR(svn_sqlite__step_done(stmt));
1120 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1121 STMT_DELETE_ACTUAL_EMPTY));
1122 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1123 SVN_ERR(svn_sqlite__step_done(stmt));
1126 if (piwb->not_present_op_depth > 0
1127 && piwb->not_present_op_depth < piwb->op_depth)
1129 /* And also insert a not-present node to tell the commit processing that
1130 a child of the parent node was not copied. */
1131 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1134 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
1135 wcroot->wc_id, local_relpath,
1136 piwb->not_present_op_depth, parent_relpath,
1137 piwb->original_repos_id,
1138 piwb->original_repos_relpath,
1139 piwb->original_revnum,
1140 presence_map, svn_wc__db_status_not_present,
1142 kind_map, piwb->kind));
1144 SVN_ERR(svn_sqlite__step_done(stmt));
1147 SVN_ERR(add_work_items(wcroot->sdb, piwb->work_items, scratch_pool));
1149 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
1150 piwb->conflict, scratch_pool));
1152 return SVN_NO_ERROR;
1156 /* Return in *CHILDREN all of the children of the directory LOCAL_RELPATH,
1157 of any status, in all op-depths in the NODES table. */
1158 static svn_error_t *
1159 gather_children(const apr_array_header_t **children,
1160 svn_wc__db_wcroot_t *wcroot,
1161 const char *parent_relpath,
1164 apr_pool_t *result_pool,
1165 apr_pool_t *scratch_pool)
1167 apr_array_header_t *result;
1168 svn_sqlite__stmt_t *stmt;
1169 svn_boolean_t have_row;
1171 result = apr_array_make(result_pool, 16, sizeof(const char*));
1173 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
1174 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
1176 SVN_ERR(svn_sqlite__bind_int(stmt, 3, op_depth));
1178 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1181 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1182 const char *name = svn_relpath_basename(child_relpath, result_pool);
1184 APR_ARRAY_PUSH(result, const char *) = name;
1186 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1189 SVN_ERR(svn_sqlite__reset(stmt));
1191 return SVN_NO_ERROR;
1194 /* Return TRUE if CHILD_ABSPATH is an immediate child of PARENT_ABSPATH.
1195 * Else, return FALSE. */
1196 static svn_boolean_t
1197 is_immediate_child_path(const char *parent_abspath, const char *child_abspath)
1199 const char *local_relpath = svn_dirent_skip_ancestor(parent_abspath,
1202 /* To be an immediate child local_relpath should have one (not empty)
1204 return local_relpath && *local_relpath && !strchr(local_relpath, '/');
1208 /* Remove the access baton for LOCAL_ABSPATH from ACCESS_CACHE. */
1210 remove_from_access_cache(apr_hash_t *access_cache,
1211 const char *local_abspath)
1213 svn_wc_adm_access_t *adm_access;
1215 adm_access = svn_hash_gets(access_cache, local_abspath);
1217 svn_wc__adm_access_set_entries(adm_access, NULL);
1221 /* Flush the access baton for LOCAL_ABSPATH, and any of its children up to
1222 * the specified DEPTH, from the access baton cache in WCROOT.
1223 * Also flush the access baton for the parent of LOCAL_ABSPATH.I
1225 * This function must be called when the access baton cache goes stale,
1226 * i.e. data about LOCAL_ABSPATH will need to be read again from disk.
1228 * Use SCRATCH_POOL for temporary allocations. */
1229 static svn_error_t *
1230 flush_entries(svn_wc__db_wcroot_t *wcroot,
1231 const char *local_abspath,
1233 apr_pool_t *scratch_pool)
1235 const char *parent_abspath;
1237 if (apr_hash_count(wcroot->access_cache) == 0)
1238 return SVN_NO_ERROR;
1240 remove_from_access_cache(wcroot->access_cache, local_abspath);
1242 if (depth > svn_depth_empty)
1244 apr_hash_index_t *hi;
1246 /* Flush access batons of children within the specified depth. */
1247 for (hi = apr_hash_first(scratch_pool, wcroot->access_cache);
1249 hi = apr_hash_next(hi))
1251 const char *item_abspath = apr_hash_this_key(hi);
1253 if ((depth == svn_depth_files || depth == svn_depth_immediates) &&
1254 is_immediate_child_path(local_abspath, item_abspath))
1256 remove_from_access_cache(wcroot->access_cache, item_abspath);
1258 else if (depth == svn_depth_infinity &&
1259 svn_dirent_is_ancestor(local_abspath, item_abspath))
1261 remove_from_access_cache(wcroot->access_cache, item_abspath);
1266 /* We're going to be overly aggressive here and just flush the parent
1267 without doing much checking. This may hurt performance for
1268 legacy API consumers, but that's not our problem. :) */
1269 parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
1270 remove_from_access_cache(wcroot->access_cache, parent_abspath);
1272 return SVN_NO_ERROR;
1276 /* Add a single WORK_ITEM into the given SDB's WORK_QUEUE table. This does
1277 not perform its work within a transaction, assuming the caller will
1279 static svn_error_t *
1280 add_single_work_item(svn_sqlite__db_t *sdb,
1281 const svn_skel_t *work_item,
1282 apr_pool_t *scratch_pool)
1284 svn_stringbuf_t *serialized;
1285 svn_sqlite__stmt_t *stmt;
1287 serialized = svn_skel__unparse(work_item, scratch_pool);
1288 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_WORK_ITEM));
1289 SVN_ERR(svn_sqlite__bind_blob(stmt, 1, serialized->data, serialized->len));
1290 return svn_error_trace(svn_sqlite__insert(NULL, stmt));
1294 /* Add work item(s) to the given SDB. Also see add_single_work_item(). This
1295 SKEL is usually passed to the various wc_db operation functions. It may
1296 be NULL, indicating no additional work items are needed, it may be a
1297 single work item, or it may be a list of work items. */
1298 static svn_error_t *
1299 add_work_items(svn_sqlite__db_t *sdb,
1300 const svn_skel_t *skel,
1301 apr_pool_t *scratch_pool)
1303 apr_pool_t *iterpool;
1305 /* Maybe there are no work items to insert. */
1307 return SVN_NO_ERROR;
1309 /* Should have a list. */
1310 SVN_ERR_ASSERT(!skel->is_atom);
1312 /* Is the list a single work item? Or a list of work items? */
1313 if (SVN_WC__SINGLE_WORK_ITEM(skel))
1314 return svn_error_trace(add_single_work_item(sdb, skel, scratch_pool));
1316 /* SKEL is a list-of-lists, aka list of work items. */
1318 iterpool = svn_pool_create(scratch_pool);
1319 for (skel = skel->children; skel; skel = skel->next)
1321 svn_pool_clear(iterpool);
1323 SVN_ERR(add_single_work_item(sdb, skel, iterpool));
1325 svn_pool_destroy(iterpool);
1327 return SVN_NO_ERROR;
1331 /* Determine whether the node exists for a given WCROOT and LOCAL_RELPATH. */
1332 static svn_error_t *
1333 does_node_exist(svn_boolean_t *exists,
1334 const svn_wc__db_wcroot_t *wcroot,
1335 const char *local_relpath)
1337 svn_sqlite__stmt_t *stmt;
1339 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DOES_NODE_EXIST));
1340 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1341 SVN_ERR(svn_sqlite__step(exists, stmt));
1343 return svn_error_trace(svn_sqlite__reset(stmt));
1347 svn_wc__db_install_schema_statistics(svn_sqlite__db_t *sdb,
1348 apr_pool_t *scratch_pool)
1350 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_INSTALL_SCHEMA_STATISTICS));
1352 return SVN_NO_ERROR;
1355 /* Helper for create_db(). Initializes our wc.db schema.
1357 static svn_error_t *
1358 init_db(/* output values */
1359 apr_int64_t *repos_id,
1362 svn_sqlite__db_t *db,
1363 const char *repos_root_url,
1364 const char *repos_uuid,
1365 const char *root_node_repos_relpath,
1366 svn_revnum_t root_node_revision,
1367 svn_depth_t root_node_depth,
1368 apr_pool_t *scratch_pool)
1370 svn_sqlite__stmt_t *stmt;
1372 /* Create the database's schema. */
1373 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_SCHEMA));
1374 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES));
1375 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES_TRIGGERS));
1376 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_EXTERNALS));
1378 SVN_ERR(svn_wc__db_install_schema_statistics(db, scratch_pool));
1380 /* Insert the repository. */
1381 SVN_ERR(create_repos_id(repos_id, repos_root_url, repos_uuid,
1384 /* Insert the wcroot. */
1385 /* ### Right now, this just assumes wc metadata is being stored locally. */
1386 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT));
1387 SVN_ERR(svn_sqlite__insert(wc_id, stmt));
1389 if (root_node_repos_relpath)
1391 svn_wc__db_status_t status = svn_wc__db_status_normal;
1393 if (root_node_revision > 0)
1394 status = svn_wc__db_status_incomplete; /* Will be filled by update */
1396 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_NODE));
1397 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtst",
1400 0, /* op_depth is 0 for base */
1401 SVN_VA_NULL, /* 4 */
1403 root_node_repos_relpath,
1405 presence_map, status, /* 8 */
1406 svn_token__to_word(depth_map,
1408 kind_map, svn_node_dir /* 10 */));
1410 SVN_ERR(svn_sqlite__insert(NULL, stmt));
1413 return SVN_NO_ERROR;
1416 /* Create an sqlite database at DIR_ABSPATH/SDB_FNAME and insert
1417 records for REPOS_ID (using REPOS_ROOT_URL and REPOS_UUID) into
1418 REPOSITORY and for WC_ID into WCROOT. Return the DB connection
1421 If ROOT_NODE_REPOS_RELPATH is not NULL, insert a BASE node at
1422 the working copy root with repository relpath ROOT_NODE_REPOS_RELPATH,
1423 revision ROOT_NODE_REVISION and depth ROOT_NODE_DEPTH.
1425 static svn_error_t *
1426 create_db(svn_sqlite__db_t **sdb,
1427 apr_int64_t *repos_id,
1429 const char *dir_abspath,
1430 const char *repos_root_url,
1431 const char *repos_uuid,
1432 const char *sdb_fname,
1433 const char *root_node_repos_relpath,
1434 svn_revnum_t root_node_revision,
1435 svn_depth_t root_node_depth,
1436 svn_boolean_t exclusive,
1437 apr_int32_t timeout,
1438 apr_pool_t *result_pool,
1439 apr_pool_t *scratch_pool)
1441 SVN_ERR(svn_wc__db_util_open_db(sdb, dir_abspath, sdb_fname,
1442 svn_sqlite__mode_rwcreate, exclusive,
1444 NULL /* my_statements */,
1445 result_pool, scratch_pool));
1447 SVN_SQLITE__WITH_LOCK(init_db(repos_id, wc_id,
1448 *sdb, repos_root_url, repos_uuid,
1449 root_node_repos_relpath, root_node_revision,
1450 root_node_depth, scratch_pool),
1453 return SVN_NO_ERROR;
1458 svn_wc__db_init(svn_wc__db_t *db,
1459 const char *local_abspath,
1460 const char *repos_relpath,
1461 const char *repos_root_url,
1462 const char *repos_uuid,
1463 svn_revnum_t initial_rev,
1465 apr_pool_t *scratch_pool)
1467 svn_sqlite__db_t *sdb;
1468 apr_int64_t repos_id;
1470 svn_wc__db_wcroot_t *wcroot;
1471 svn_boolean_t sqlite_exclusive = FALSE;
1472 apr_int32_t sqlite_timeout = 0; /* default timeout */
1473 apr_hash_index_t *hi;
1475 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1476 SVN_ERR_ASSERT(repos_relpath != NULL);
1477 SVN_ERR_ASSERT(depth == svn_depth_empty
1478 || depth == svn_depth_files
1479 || depth == svn_depth_immediates
1480 || depth == svn_depth_infinity);
1482 /* ### REPOS_ROOT_URL and REPOS_UUID may be NULL. ... more doc: tbd */
1484 SVN_ERR(svn_config_get_bool(db->config, &sqlite_exclusive,
1485 SVN_CONFIG_SECTION_WORKING_COPY,
1486 SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
1489 /* Create the SDB and insert the basic rows. */
1490 SVN_ERR(create_db(&sdb, &repos_id, &wc_id, local_abspath, repos_root_url,
1491 repos_uuid, SDB_FILE,
1492 repos_relpath, initial_rev, depth, sqlite_exclusive,
1494 db->state_pool, scratch_pool));
1496 /* Create the WCROOT for this directory. */
1497 SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
1498 apr_pstrdup(db->state_pool, local_abspath),
1499 sdb, wc_id, FORMAT_FROM_SDB,
1500 FALSE /* auto-upgrade */,
1501 db->state_pool, scratch_pool));
1503 /* Any previously cached children may now have a new WCROOT, most likely that
1504 of the new WCROOT, but there might be descendant directories that are their
1505 own working copy, in which case setting WCROOT to our new WCROOT might
1506 actually break things for those.
1508 Clearing is the safest thing we can do in this case, as a test would lead
1509 to unnecessary probing, while the standard code probes later anyway. So we
1510 only lose a bit of memory
1512 ### Perhaps we could check wcroot->abspath to detect which case we have
1513 where, but currently it is already very hard to trigger this from
1514 the short living 'svn' client. (GUI clients like TortoiseSVN are far
1515 more likely to get in these cases)
1517 for (hi = apr_hash_first(scratch_pool, db->dir_data);
1519 hi = apr_hash_next(hi))
1521 const char *abspath = apr_hash_this_key(hi);
1522 if (svn_dirent_is_ancestor(wcroot->abspath, abspath))
1523 svn_hash_sets(db->dir_data, abspath, NULL);
1526 /* The WCROOT is complete. Stash it into DB. */
1527 svn_hash_sets(db->dir_data, wcroot->abspath, wcroot);
1529 return SVN_NO_ERROR;
1534 svn_wc__db_to_relpath(const char **local_relpath,
1536 const char *wri_abspath,
1537 const char *local_abspath,
1538 apr_pool_t *result_pool,
1539 apr_pool_t *scratch_pool)
1541 svn_wc__db_wcroot_t *wcroot;
1542 const char *relpath;
1544 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1546 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &relpath, db,
1547 wri_abspath, result_pool, scratch_pool));
1549 /* This function is indirectly called from the upgrade code, so we
1550 can't verify the wcroot here. Just check that it is not NULL */
1551 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1553 if (svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
1555 *local_relpath = apr_pstrdup(result_pool,
1556 svn_dirent_skip_ancestor(wcroot->abspath,
1560 /* Probably moving from $TMP. Should we allow this? */
1561 *local_relpath = apr_pstrdup(result_pool, local_abspath);
1563 return SVN_NO_ERROR;
1568 svn_wc__db_from_relpath(const char **local_abspath,
1570 const char *wri_abspath,
1571 const char *local_relpath,
1572 apr_pool_t *result_pool,
1573 apr_pool_t *scratch_pool)
1575 svn_wc__db_wcroot_t *wcroot;
1576 const char *unused_relpath;
1578 SVN_ERR_ASSERT(svn_relpath_is_canonical(local_relpath));
1581 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1582 wri_abspath, scratch_pool, scratch_pool));
1584 /* This function is indirectly called from the upgrade code, so we
1585 can't verify the wcroot here. Just check that it is not NULL */
1586 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1589 *local_abspath = svn_dirent_join(wcroot->abspath,
1592 return SVN_NO_ERROR;
1597 svn_wc__db_get_wcroot(const char **wcroot_abspath,
1599 const char *wri_abspath,
1600 apr_pool_t *result_pool,
1601 apr_pool_t *scratch_pool)
1603 svn_wc__db_wcroot_t *wcroot;
1604 const char *unused_relpath;
1606 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1607 wri_abspath, scratch_pool, scratch_pool));
1609 /* Can't use VERIFY_USABLE_WCROOT, as this should be usable to detect
1610 where call upgrade */
1611 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1613 *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
1615 return SVN_NO_ERROR;
1620 svn_wc__db_base_add_directory(svn_wc__db_t *db,
1621 const char *local_abspath,
1622 const char *wri_abspath,
1623 const char *repos_relpath,
1624 const char *repos_root_url,
1625 const char *repos_uuid,
1626 svn_revnum_t revision,
1627 const apr_hash_t *props,
1628 svn_revnum_t changed_rev,
1629 apr_time_t changed_date,
1630 const char *changed_author,
1631 const apr_array_header_t *children,
1633 apr_hash_t *dav_cache,
1634 svn_boolean_t update_actual_props,
1635 apr_hash_t *new_actual_props,
1636 apr_array_header_t *new_iprops,
1637 const svn_skel_t *conflict,
1638 const svn_skel_t *work_items,
1639 apr_pool_t *scratch_pool)
1641 svn_wc__db_wcroot_t *wcroot;
1642 const char *local_relpath;
1643 insert_base_baton_t ibb;
1645 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1646 SVN_ERR_ASSERT(repos_relpath != NULL);
1647 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1648 SVN_ERR_ASSERT(repos_uuid != NULL);
1649 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1650 SVN_ERR_ASSERT(props != NULL);
1651 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1653 SVN_ERR_ASSERT(children != NULL);
1656 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1657 wri_abspath, scratch_pool, scratch_pool));
1658 VERIFY_USABLE_WCROOT(wcroot);
1659 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1663 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1664 ibb.repos_root_url = repos_root_url;
1665 ibb.repos_uuid = repos_uuid;
1667 ibb.status = svn_wc__db_status_normal;
1668 ibb.kind = svn_node_dir;
1669 ibb.repos_relpath = repos_relpath;
1670 ibb.revision = revision;
1672 ibb.iprops = new_iprops;
1674 ibb.changed_rev = changed_rev;
1675 ibb.changed_date = changed_date;
1676 ibb.changed_author = changed_author;
1678 ibb.children = children;
1681 ibb.dav_cache = dav_cache;
1682 ibb.conflict = conflict;
1683 ibb.work_items = work_items;
1685 if (update_actual_props)
1687 ibb.update_actual_props = TRUE;
1688 ibb.new_actual_props = new_actual_props;
1691 /* Insert the directory and all its children transactionally.
1693 Note: old children can stick around, even if they are no longer present
1694 in this directory's revision. */
1695 SVN_WC__DB_WITH_TXN(
1696 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1699 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
1700 return SVN_NO_ERROR;
1704 svn_wc__db_base_add_incomplete_directory(svn_wc__db_t *db,
1705 const char *local_abspath,
1706 const char *repos_relpath,
1707 const char *repos_root_url,
1708 const char *repos_uuid,
1709 svn_revnum_t revision,
1711 svn_boolean_t insert_base_deleted,
1712 svn_boolean_t delete_working,
1713 svn_skel_t *conflict,
1714 svn_skel_t *work_items,
1715 apr_pool_t *scratch_pool)
1717 svn_wc__db_wcroot_t *wcroot;
1718 const char *local_relpath;
1719 struct insert_base_baton_t ibb;
1721 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1722 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1723 SVN_ERR_ASSERT(repos_relpath && repos_root_url && repos_uuid);
1725 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
1727 scratch_pool, scratch_pool));
1729 VERIFY_USABLE_WCROOT(wcroot);
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_incomplete;
1738 ibb.kind = svn_node_dir;
1739 ibb.repos_relpath = repos_relpath;
1740 ibb.revision = revision;
1742 ibb.insert_base_deleted = insert_base_deleted;
1743 ibb.delete_working = delete_working;
1745 ibb.conflict = conflict;
1746 ibb.work_items = work_items;
1748 SVN_WC__DB_WITH_TXN(
1749 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1752 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
1754 return SVN_NO_ERROR;
1759 svn_wc__db_base_add_file(svn_wc__db_t *db,
1760 const char *local_abspath,
1761 const char *wri_abspath,
1762 const char *repos_relpath,
1763 const char *repos_root_url,
1764 const char *repos_uuid,
1765 svn_revnum_t revision,
1766 const apr_hash_t *props,
1767 svn_revnum_t changed_rev,
1768 apr_time_t changed_date,
1769 const char *changed_author,
1770 const svn_checksum_t *checksum,
1771 apr_hash_t *dav_cache,
1772 svn_boolean_t delete_working,
1773 svn_boolean_t update_actual_props,
1774 apr_hash_t *new_actual_props,
1775 apr_array_header_t *new_iprops,
1776 svn_boolean_t keep_recorded_info,
1777 svn_boolean_t insert_base_deleted,
1778 const svn_skel_t *conflict,
1779 const svn_skel_t *work_items,
1780 apr_pool_t *scratch_pool)
1782 svn_wc__db_wcroot_t *wcroot;
1783 const char *local_relpath;
1784 insert_base_baton_t ibb;
1786 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1787 SVN_ERR_ASSERT(repos_relpath != NULL);
1788 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1789 SVN_ERR_ASSERT(repos_uuid != NULL);
1790 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1791 SVN_ERR_ASSERT(props != NULL);
1792 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1793 SVN_ERR_ASSERT(checksum != NULL);
1795 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1796 wri_abspath, scratch_pool, scratch_pool));
1797 VERIFY_USABLE_WCROOT(wcroot);
1798 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1802 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1803 ibb.repos_root_url = repos_root_url;
1804 ibb.repos_uuid = repos_uuid;
1806 ibb.status = svn_wc__db_status_normal;
1807 ibb.kind = svn_node_file;
1808 ibb.repos_relpath = repos_relpath;
1809 ibb.revision = revision;
1812 ibb.changed_rev = changed_rev;
1813 ibb.changed_date = changed_date;
1814 ibb.changed_author = changed_author;
1816 ibb.checksum = checksum;
1818 ibb.dav_cache = dav_cache;
1819 ibb.iprops = new_iprops;
1821 if (update_actual_props)
1823 ibb.update_actual_props = TRUE;
1824 ibb.new_actual_props = new_actual_props;
1827 ibb.keep_recorded_info = keep_recorded_info;
1828 ibb.insert_base_deleted = insert_base_deleted;
1829 ibb.delete_working = delete_working;
1831 ibb.conflict = conflict;
1832 ibb.work_items = work_items;
1834 SVN_WC__DB_WITH_TXN(
1835 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1838 /* If this used to be a directory we should remove children so pass
1839 * depth infinity. */
1840 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1842 return SVN_NO_ERROR;
1847 svn_wc__db_base_add_symlink(svn_wc__db_t *db,
1848 const char *local_abspath,
1849 const char *wri_abspath,
1850 const char *repos_relpath,
1851 const char *repos_root_url,
1852 const char *repos_uuid,
1853 svn_revnum_t revision,
1854 const apr_hash_t *props,
1855 svn_revnum_t changed_rev,
1856 apr_time_t changed_date,
1857 const char *changed_author,
1859 apr_hash_t *dav_cache,
1860 svn_boolean_t delete_working,
1861 svn_boolean_t update_actual_props,
1862 apr_hash_t *new_actual_props,
1863 apr_array_header_t *new_iprops,
1864 svn_boolean_t keep_recorded_info,
1865 svn_boolean_t insert_base_deleted,
1866 const svn_skel_t *conflict,
1867 const svn_skel_t *work_items,
1868 apr_pool_t *scratch_pool)
1870 svn_wc__db_wcroot_t *wcroot;
1871 const char *local_relpath;
1872 insert_base_baton_t ibb;
1874 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1875 SVN_ERR_ASSERT(repos_relpath != NULL);
1876 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1877 SVN_ERR_ASSERT(repos_uuid != NULL);
1878 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1879 SVN_ERR_ASSERT(props != NULL);
1880 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1881 SVN_ERR_ASSERT(target != NULL);
1883 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1884 wri_abspath, scratch_pool, scratch_pool));
1885 VERIFY_USABLE_WCROOT(wcroot);
1886 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1889 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1890 ibb.repos_root_url = repos_root_url;
1891 ibb.repos_uuid = repos_uuid;
1893 ibb.status = svn_wc__db_status_normal;
1894 ibb.kind = svn_node_symlink;
1895 ibb.repos_relpath = repos_relpath;
1896 ibb.revision = revision;
1899 ibb.changed_rev = changed_rev;
1900 ibb.changed_date = changed_date;
1901 ibb.changed_author = changed_author;
1903 ibb.target = target;
1905 ibb.dav_cache = dav_cache;
1906 ibb.iprops = new_iprops;
1908 if (update_actual_props)
1910 ibb.update_actual_props = TRUE;
1911 ibb.new_actual_props = new_actual_props;
1914 ibb.keep_recorded_info = keep_recorded_info;
1915 ibb.insert_base_deleted = insert_base_deleted;
1916 ibb.delete_working = delete_working;
1918 ibb.conflict = conflict;
1919 ibb.work_items = work_items;
1921 SVN_WC__DB_WITH_TXN(
1922 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1925 /* If this used to be a directory we should remove children so pass
1926 * depth infinity. */
1927 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1929 return SVN_NO_ERROR;
1933 static svn_error_t *
1934 add_excluded_or_not_present_node(svn_wc__db_t *db,
1935 const char *local_abspath,
1936 const char *repos_relpath,
1937 const char *repos_root_url,
1938 const char *repos_uuid,
1939 svn_revnum_t revision,
1940 svn_node_kind_t kind,
1941 svn_wc__db_status_t status,
1942 const svn_skel_t *conflict,
1943 const svn_skel_t *work_items,
1944 apr_pool_t *scratch_pool)
1946 svn_wc__db_wcroot_t *wcroot;
1947 const char *local_relpath;
1948 insert_base_baton_t ibb;
1949 const char *dir_abspath, *name;
1951 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1952 SVN_ERR_ASSERT(repos_relpath != NULL);
1953 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1954 SVN_ERR_ASSERT(repos_uuid != NULL);
1955 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1956 SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
1957 || status == svn_wc__db_status_excluded
1958 || status == svn_wc__db_status_not_present);
1960 /* These absent presence nodes are only useful below a parent node that is
1961 present. To avoid problems with working copies obstructing the child
1962 we calculate the wcroot and local_relpath of the parent and then add
1965 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
1967 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1968 dir_abspath, scratch_pool, scratch_pool));
1969 VERIFY_USABLE_WCROOT(wcroot);
1971 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
1975 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1976 ibb.repos_root_url = repos_root_url;
1977 ibb.repos_uuid = repos_uuid;
1979 ibb.status = status;
1981 ibb.repos_relpath = repos_relpath;
1982 ibb.revision = revision;
1984 /* Depending upon KIND, any of these might get used. */
1985 ibb.children = NULL;
1986 ibb.depth = svn_depth_unknown;
1987 ibb.checksum = NULL;
1990 ibb.conflict = conflict;
1991 ibb.work_items = work_items;
1993 SVN_WC__DB_WITH_TXN(
1994 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1997 /* If this used to be a directory we should remove children so pass
1998 * depth infinity. */
1999 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2002 return SVN_NO_ERROR;
2007 svn_wc__db_base_add_excluded_node(svn_wc__db_t *db,
2008 const char *local_abspath,
2009 const char *repos_relpath,
2010 const char *repos_root_url,
2011 const char *repos_uuid,
2012 svn_revnum_t revision,
2013 svn_node_kind_t kind,
2014 svn_wc__db_status_t status,
2015 const svn_skel_t *conflict,
2016 const svn_skel_t *work_items,
2017 apr_pool_t *scratch_pool)
2019 SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
2020 || status == svn_wc__db_status_excluded);
2022 return add_excluded_or_not_present_node(
2023 db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2024 kind, status, conflict, work_items, scratch_pool);
2029 svn_wc__db_base_add_not_present_node(svn_wc__db_t *db,
2030 const char *local_abspath,
2031 const char *repos_relpath,
2032 const char *repos_root_url,
2033 const char *repos_uuid,
2034 svn_revnum_t revision,
2035 svn_node_kind_t kind,
2036 const svn_skel_t *conflict,
2037 const svn_skel_t *work_items,
2038 apr_pool_t *scratch_pool)
2040 return add_excluded_or_not_present_node(
2041 db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2042 kind, svn_wc__db_status_not_present, conflict, work_items, scratch_pool);
2045 /* Recursively clear moved-here information at the copy-half of the move
2046 * which moved a node to MOVED_TO_RELPATH. This transforms this side of the
2047 * move into a simple copy.
2049 static svn_error_t *
2050 clear_moved_here(svn_wc__db_wcroot_t *wcroot,
2051 const char *moved_to_relpath,
2052 apr_pool_t *scratch_pool)
2054 svn_sqlite__stmt_t *stmt;
2057 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2058 STMT_CLEAR_MOVED_HERE_RECURSIVE));
2059 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, moved_to_relpath,
2060 relpath_depth(moved_to_relpath)));
2062 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2064 if (affected_rows == 0)
2065 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2066 _("The node '%s' was not found."),
2067 path_for_error_message(wcroot, moved_to_relpath,
2070 return SVN_NO_ERROR;
2074 svn_wc__db_op_break_move_internal(svn_wc__db_wcroot_t *wcroot,
2075 const char *src_relpath,
2076 int delete_op_depth,
2077 const char *dst_relpath,
2078 const svn_skel_t *work_items,
2079 apr_pool_t *scratch_pool)
2081 svn_sqlite__stmt_t *stmt;
2084 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2085 STMT_CLEAR_MOVED_TO_RELPATH));
2086 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, src_relpath,
2088 SVN_ERR(svn_sqlite__update(&affected, stmt));
2091 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2092 _("Path '%s' is not moved"),
2093 path_for_error_message(wcroot, src_relpath,
2096 SVN_ERR(clear_moved_here(wcroot, dst_relpath, scratch_pool));
2098 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2099 return SVN_NO_ERROR;
2103 /* The body of svn_wc__db_base_remove().
2105 static svn_error_t *
2106 db_base_remove(svn_wc__db_wcroot_t *wcroot,
2107 const char *local_relpath,
2108 svn_wc__db_t *db, /* For checking conflicts */
2109 svn_boolean_t keep_as_working,
2110 svn_boolean_t mark_not_present,
2111 svn_boolean_t mark_excluded,
2112 svn_revnum_t marker_revision,
2113 svn_skel_t *conflict,
2114 svn_skel_t *work_items,
2115 apr_pool_t *scratch_pool)
2117 svn_sqlite__stmt_t *stmt;
2118 svn_boolean_t have_row;
2119 svn_wc__db_status_t status;
2120 svn_revnum_t revision;
2121 apr_int64_t repos_id;
2122 const char *repos_relpath;
2123 svn_node_kind_t kind;
2124 svn_boolean_t keep_working;
2126 svn_node_kind_t wrk_kind;
2127 svn_boolean_t no_delete_wc = FALSE;
2128 svn_boolean_t file_external;
2130 SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, &revision,
2131 &repos_relpath, &repos_id,
2132 NULL, NULL, NULL, NULL, NULL,
2133 NULL, NULL, NULL, NULL,
2135 wcroot, local_relpath,
2136 scratch_pool, scratch_pool));
2138 /* Check if there is already a working node */
2139 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2140 STMT_SELECT_NODE_INFO));
2141 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2142 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2145 return svn_error_trace(svn_sqlite__reset(stmt)); /* No BASE */
2147 op_depth = svn_sqlite__column_int(stmt, 0);
2148 wrk_kind = svn_sqlite__column_token(stmt, 4, kind_map);
2151 && op_depth == relpath_depth(local_relpath))
2153 svn_wc__db_status_t presence;
2154 presence = svn_sqlite__column_token(stmt, 3, presence_map);
2156 if (presence == svn_wc__db_status_base_deleted)
2158 keep_working = FALSE;
2159 no_delete_wc = TRUE;
2163 keep_working = TRUE;
2167 keep_working = FALSE;
2168 SVN_ERR(svn_sqlite__reset(stmt));
2170 if (keep_as_working && op_depth == 0)
2172 if (status == svn_wc__db_status_normal
2173 || status == svn_wc__db_status_incomplete)
2175 SVN_ERR(svn_wc__db_op_make_copy_internal(wcroot, local_relpath, TRUE,
2179 keep_working = TRUE;
2182 /* Step 1: Create workqueue operations to remove files and dirs in the
2184 if (!keep_working && !no_delete_wc)
2186 svn_skel_t *work_item;
2187 const char *local_abspath;
2189 local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
2191 if (wrk_kind == svn_node_dir)
2193 apr_pool_t *iterpool;
2194 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2195 STMT_SELECT_WORKING_PRESENT));
2196 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2198 iterpool = svn_pool_create(scratch_pool);
2200 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2204 const char *node_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2205 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 1,
2207 const char *node_abspath;
2210 svn_pool_clear(iterpool);
2212 node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
2215 if (node_kind == svn_node_dir)
2216 err = svn_wc__wq_build_dir_remove(&work_item,
2217 db, wcroot->abspath,
2218 node_abspath, FALSE,
2219 iterpool, iterpool);
2221 err = svn_wc__wq_build_file_remove(&work_item,
2225 iterpool, iterpool);
2228 err = add_work_items(wcroot->sdb, work_item, iterpool);
2230 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2232 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2235 SVN_ERR(svn_sqlite__reset(stmt));
2237 SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
2238 db, wcroot->abspath,
2239 local_abspath, FALSE,
2240 scratch_pool, iterpool));
2241 svn_pool_destroy(iterpool);
2244 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
2245 db, wcroot->abspath,
2247 scratch_pool, scratch_pool));
2249 SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
2252 /* Step 2: Delete ACTUAL nodes */
2255 /* There won't be a record in NODE left for this node, so we want
2256 to remove *all* ACTUAL nodes, including ACTUAL ONLY. */
2257 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2258 STMT_DELETE_ACTUAL_NODE_RECURSIVE));
2259 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2260 SVN_ERR(svn_sqlite__step_done(stmt));
2262 else if (! keep_as_working)
2264 /* Delete only the ACTUAL nodes that apply to a delete of a BASE node */
2265 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2266 STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE));
2267 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2268 SVN_ERR(svn_sqlite__step_done(stmt));
2270 /* Else: Everything has been turned into a copy, so we want to keep all
2271 ACTUAL_NODE records */
2273 /* Step 3: Delete WORKING nodes */
2276 apr_pool_t *iterpool;
2278 /* When deleting everything in working we should break moves from
2281 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2282 STMT_SELECT_MOVED_OUTSIDE));
2283 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2285 relpath_depth(local_relpath)));
2286 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2287 iterpool = svn_pool_create(scratch_pool);
2290 const char *moved_to_relpath;
2293 svn_pool_clear(iterpool);
2294 moved_to_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
2295 err = clear_moved_here(wcroot, moved_to_relpath, iterpool);
2297 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2298 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2300 svn_pool_destroy(iterpool);
2301 SVN_ERR(svn_sqlite__reset(stmt));
2305 /* We are keeping things that are in WORKING, but we should still
2306 break moves of things in BASE. (Mixed revisions make it
2307 impossible to guarantee that we can keep everything moved) */
2309 apr_pool_t *iterpool;
2311 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2312 STMT_SELECT_MOVED_DESCENDANTS_SRC));
2313 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2315 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2316 iterpool = svn_pool_create(scratch_pool);
2319 int delete_op_depth = svn_sqlite__column_int(stmt, 0);
2320 const char *src_relpath;
2321 const char *dst_relpath;
2324 svn_pool_clear(iterpool);
2326 src_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
2327 dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool);
2329 err = svn_wc__db_op_break_move_internal(wcroot, src_relpath,
2336 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2338 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2340 svn_pool_destroy(iterpool);
2341 SVN_ERR(svn_sqlite__reset(stmt));
2345 SVN_ERR(svn_sqlite__get_statement(
2347 STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE));
2348 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
2349 SVN_ERR(svn_sqlite__step_done(stmt));
2353 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2354 STMT_DELETE_WORKING_RECURSIVE));
2355 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2356 SVN_ERR(svn_sqlite__step_done(stmt));
2359 /* Step 4: Delete the BASE node descendants */
2360 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2361 STMT_DELETE_BASE_RECURSIVE));
2362 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2363 SVN_ERR(svn_sqlite__step_done(stmt));
2365 SVN_ERR(db_retract_parent_delete(wcroot, local_relpath, 0, scratch_pool));
2367 if (mark_not_present || mark_excluded)
2369 struct insert_base_baton_t ibb;
2370 svn_boolean_t no_marker = FALSE;
2374 const char *parent_local_relpath;
2378 /* For file externals we only want to place a not present marker
2379 if there is a BASE parent */
2381 svn_relpath_split(&parent_local_relpath, &name, local_relpath,
2384 err = svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
2385 &repos_relpath, &repos_id,
2386 NULL, NULL, NULL, NULL, NULL,
2387 NULL, NULL, NULL, NULL, NULL,
2388 wcroot, parent_local_relpath,
2389 scratch_pool, scratch_pool);
2391 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2392 return svn_error_trace(err);
2395 svn_error_clear(err);
2400 /* Replace the repos_relpath with something more expected than
2401 the unrelated old file external repository relpath, which
2402 one day may come from a different repository */
2403 repos_relpath = svn_relpath_join(repos_relpath, name, scratch_pool);
2411 ibb.repos_id = repos_id;
2412 ibb.status = mark_excluded ? svn_wc__db_status_excluded
2413 : svn_wc__db_status_not_present;
2415 ibb.repos_relpath = repos_relpath;
2416 ibb.revision = SVN_IS_VALID_REVNUM(marker_revision)
2420 /* Depending upon KIND, any of these might get used. */
2421 ibb.children = NULL;
2422 ibb.depth = svn_depth_unknown;
2423 ibb.checksum = NULL;
2426 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
2430 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2432 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
2433 conflict, scratch_pool));
2435 return SVN_NO_ERROR;
2440 svn_wc__db_base_remove(svn_wc__db_t *db,
2441 const char *local_abspath,
2442 svn_boolean_t keep_as_working,
2443 svn_boolean_t mark_not_present,
2444 svn_boolean_t mark_excluded,
2445 svn_revnum_t marker_revision,
2446 svn_skel_t *conflict,
2447 svn_skel_t *work_items,
2448 apr_pool_t *scratch_pool)
2450 svn_wc__db_wcroot_t *wcroot;
2451 const char *local_relpath;
2453 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2455 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2456 local_abspath, scratch_pool, scratch_pool));
2457 VERIFY_USABLE_WCROOT(wcroot);
2459 SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath,
2460 db, keep_as_working,
2461 mark_not_present, mark_excluded,
2463 conflict, work_items, scratch_pool),
2466 /* If this used to be a directory we should remove children so pass
2467 * depth infinity. */
2468 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2471 return SVN_NO_ERROR;
2476 svn_wc__db_base_get_info_internal(svn_wc__db_status_t *status,
2477 svn_node_kind_t *kind,
2478 svn_revnum_t *revision,
2479 const char **repos_relpath,
2480 apr_int64_t *repos_id,
2481 svn_revnum_t *changed_rev,
2482 apr_time_t *changed_date,
2483 const char **changed_author,
2485 const svn_checksum_t **checksum,
2486 const char **target,
2487 svn_wc__db_lock_t **lock,
2488 svn_boolean_t *had_props,
2490 svn_boolean_t *update_root,
2491 svn_wc__db_wcroot_t *wcroot,
2492 const char *local_relpath,
2493 apr_pool_t *result_pool,
2494 apr_pool_t *scratch_pool)
2496 svn_sqlite__stmt_t *stmt;
2497 svn_boolean_t have_row;
2498 svn_error_t *err = SVN_NO_ERROR;
2500 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2501 lock ? STMT_SELECT_BASE_NODE_WITH_LOCK
2502 : STMT_SELECT_BASE_NODE));
2503 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2504 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2508 svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2510 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2518 *status = node_status;
2520 repos_location_from_columns(repos_id, revision, repos_relpath,
2521 stmt, 0, 4, 1, result_pool);
2522 SVN_ERR_ASSERT(!repos_id || *repos_id != INVALID_REPOS_ID);
2523 SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
2526 *lock = lock_from_columns(stmt, 15, 16, 17, 18, result_pool);
2530 *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2534 *changed_date = svn_sqlite__column_int64(stmt, 8);
2538 /* Result may be NULL. */
2539 *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2543 if (node_kind != svn_node_dir)
2545 *depth = svn_depth_unknown;
2549 *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2555 if (node_kind != svn_node_file)
2561 err = svn_sqlite__column_checksum(checksum, stmt, 5,
2564 err = svn_error_createf(
2566 _("The node '%s' has a corrupt checksum value."),
2567 path_for_error_message(wcroot, local_relpath,
2573 if (node_kind != svn_node_symlink)
2576 *target = svn_sqlite__column_text(stmt, 11, result_pool);
2580 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2584 if (node_status == svn_wc__db_status_normal
2585 || node_status == svn_wc__db_status_incomplete)
2587 SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2588 result_pool, scratch_pool));
2590 *props = apr_hash_make(result_pool);
2594 assert(svn_sqlite__column_is_null(stmt, 13));
2600 /* It's an update root iff it's a file external. */
2601 *update_root = svn_sqlite__column_boolean(stmt, 14);
2606 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2607 _("The node '%s' was not found."),
2608 path_for_error_message(wcroot, local_relpath,
2612 /* Note: given the composition, no need to wrap for tracing. */
2613 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2618 svn_wc__db_base_get_info(svn_wc__db_status_t *status,
2619 svn_node_kind_t *kind,
2620 svn_revnum_t *revision,
2621 const char **repos_relpath,
2622 const char **repos_root_url,
2623 const char **repos_uuid,
2624 svn_revnum_t *changed_rev,
2625 apr_time_t *changed_date,
2626 const char **changed_author,
2628 const svn_checksum_t **checksum,
2629 const char **target,
2630 svn_wc__db_lock_t **lock,
2631 svn_boolean_t *had_props,
2633 svn_boolean_t *update_root,
2635 const char *local_abspath,
2636 apr_pool_t *result_pool,
2637 apr_pool_t *scratch_pool)
2639 svn_wc__db_wcroot_t *wcroot;
2640 const char *local_relpath;
2641 apr_int64_t repos_id;
2643 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2645 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2646 local_abspath, scratch_pool, scratch_pool));
2647 VERIFY_USABLE_WCROOT(wcroot);
2649 SVN_WC__DB_WITH_TXN4(
2650 svn_wc__db_base_get_info_internal(status, kind, revision,
2651 repos_relpath, &repos_id,
2652 changed_rev, changed_date,
2653 changed_author, depth,
2654 checksum, target, lock,
2655 had_props, props, update_root,
2656 wcroot, local_relpath,
2657 result_pool, scratch_pool),
2658 svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
2659 wcroot, repos_id, result_pool),
2663 SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
2665 return SVN_NO_ERROR;
2668 /* The implementation of svn_wc__db_base_get_children_info */
2669 static svn_error_t *
2670 base_get_children_info(apr_hash_t **nodes,
2671 svn_wc__db_wcroot_t *wcroot,
2672 const char *local_relpath,
2673 svn_boolean_t obtain_locks,
2674 apr_pool_t *result_pool,
2675 apr_pool_t *scratch_pool)
2677 svn_sqlite__stmt_t *stmt;
2678 svn_boolean_t have_row;
2679 apr_int64_t last_repos_id = INVALID_REPOS_ID;
2680 const char *last_repos_root_url = NULL;
2682 *nodes = apr_hash_make(result_pool);
2684 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2686 ? STMT_SELECT_BASE_CHILDREN_INFO_LOCK
2687 : STMT_SELECT_BASE_CHILDREN_INFO));
2688 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2690 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2694 struct svn_wc__db_base_info_t *info;
2695 apr_int64_t repos_id;
2696 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2697 const char *name = svn_relpath_basename(child_relpath, result_pool);
2699 info = apr_pcalloc(result_pool, sizeof(*info));
2701 repos_id = svn_sqlite__column_int64(stmt, 1);
2702 info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
2703 info->status = svn_sqlite__column_token(stmt, 3, presence_map);
2704 info->kind = svn_sqlite__column_token(stmt, 4, kind_map);
2705 info->revnum = svn_sqlite__column_revnum(stmt, 5);
2707 info->depth = svn_sqlite__column_token_null(stmt, 6, depth_map,
2710 info->update_root = svn_sqlite__column_boolean(stmt, 7);
2713 info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool);
2715 if (repos_id != last_repos_id)
2719 err = svn_wc__db_fetch_repos_info(&last_repos_root_url, NULL,
2724 return svn_error_trace(
2725 svn_error_compose_create(err,
2726 svn_sqlite__reset(stmt)));
2728 last_repos_id = repos_id;
2731 info->repos_root_url = last_repos_root_url;
2733 svn_hash_sets(*nodes, name, info);
2735 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2738 SVN_ERR(svn_sqlite__reset(stmt));
2740 return SVN_NO_ERROR;
2744 svn_wc__db_base_get_children_info(apr_hash_t **nodes,
2746 const char *dir_abspath,
2747 apr_pool_t *result_pool,
2748 apr_pool_t *scratch_pool)
2750 svn_wc__db_wcroot_t *wcroot;
2751 const char *local_relpath;
2753 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
2755 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2756 dir_abspath, scratch_pool, scratch_pool));
2757 VERIFY_USABLE_WCROOT(wcroot);
2759 return svn_error_trace(base_get_children_info(nodes,
2762 TRUE /* obtain_locks */,
2769 svn_wc__db_base_get_props(apr_hash_t **props,
2771 const char *local_abspath,
2772 apr_pool_t *result_pool,
2773 apr_pool_t *scratch_pool)
2775 svn_wc__db_status_t presence;
2777 SVN_ERR(svn_wc__db_base_get_info(&presence, NULL, NULL, NULL, NULL,
2778 NULL, NULL, NULL, NULL, NULL,
2779 NULL, NULL, NULL, NULL, props, NULL,
2781 result_pool, scratch_pool));
2782 if (presence != svn_wc__db_status_normal
2783 && presence != svn_wc__db_status_incomplete)
2785 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2786 _("The node '%s' has a BASE status that"
2787 " has no properties."),
2788 svn_dirent_local_style(local_abspath,
2792 return SVN_NO_ERROR;
2797 svn_wc__db_base_get_children(const apr_array_header_t **children,
2799 const char *local_abspath,
2800 apr_pool_t *result_pool,
2801 apr_pool_t *scratch_pool)
2803 svn_wc__db_wcroot_t *wcroot;
2804 const char *local_relpath;
2806 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2808 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2810 scratch_pool, scratch_pool));
2811 VERIFY_USABLE_WCROOT(wcroot);
2813 return svn_error_trace(
2814 gather_children(children, wcroot, local_relpath,
2815 STMT_SELECT_OP_DEPTH_CHILDREN, 0,
2816 result_pool, scratch_pool));
2821 svn_wc__db_base_set_dav_cache(svn_wc__db_t *db,
2822 const char *local_abspath,
2823 const apr_hash_t *props,
2824 apr_pool_t *scratch_pool)
2826 svn_wc__db_wcroot_t *wcroot;
2827 const char *local_relpath;
2828 svn_sqlite__stmt_t *stmt;
2831 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2833 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2834 local_abspath, scratch_pool, scratch_pool));
2835 VERIFY_USABLE_WCROOT(wcroot);
2837 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2838 STMT_UPDATE_BASE_NODE_DAV_CACHE));
2839 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2840 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
2842 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2844 if (affected_rows != 1)
2845 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2846 _("The node '%s' was not found."),
2847 svn_dirent_local_style(local_abspath,
2850 return SVN_NO_ERROR;
2855 svn_wc__db_base_get_dav_cache(apr_hash_t **props,
2857 const char *local_abspath,
2858 apr_pool_t *result_pool,
2859 apr_pool_t *scratch_pool)
2861 svn_wc__db_wcroot_t *wcroot;
2862 const char *local_relpath;
2863 svn_sqlite__stmt_t *stmt;
2864 svn_boolean_t have_row;
2866 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2868 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2869 local_abspath, scratch_pool, scratch_pool));
2870 VERIFY_USABLE_WCROOT(wcroot);
2872 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2873 STMT_SELECT_BASE_DAV_CACHE));
2875 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2877 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
2878 svn_sqlite__reset(stmt),
2879 _("The node '%s' was not found."),
2880 svn_dirent_local_style(local_abspath,
2883 SVN_ERR(svn_sqlite__column_properties(props, stmt, 0, result_pool,
2885 return svn_error_trace(svn_sqlite__reset(stmt));
2890 svn_wc__db_base_clear_dav_cache_recursive(svn_wc__db_t *db,
2891 const char *local_abspath,
2892 apr_pool_t *scratch_pool)
2894 svn_wc__db_wcroot_t *wcroot;
2895 const char *local_relpath;
2896 svn_sqlite__stmt_t *stmt;
2898 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2900 scratch_pool, scratch_pool));
2901 VERIFY_USABLE_WCROOT(wcroot);
2903 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2904 STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE));
2905 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2907 SVN_ERR(svn_sqlite__step_done(stmt));
2909 return SVN_NO_ERROR;
2914 svn_wc__db_depth_get_info(svn_wc__db_status_t *status,
2915 svn_node_kind_t *kind,
2916 svn_revnum_t *revision,
2917 const char **repos_relpath,
2918 apr_int64_t *repos_id,
2919 svn_revnum_t *changed_rev,
2920 apr_time_t *changed_date,
2921 const char **changed_author,
2923 const svn_checksum_t **checksum,
2924 const char **target,
2925 svn_boolean_t *had_props,
2927 svn_wc__db_wcroot_t *wcroot,
2928 const char *local_relpath,
2930 apr_pool_t *result_pool,
2931 apr_pool_t *scratch_pool)
2933 svn_sqlite__stmt_t *stmt;
2934 svn_boolean_t have_row;
2935 svn_error_t *err = SVN_NO_ERROR;
2937 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2938 STMT_SELECT_DEPTH_NODE));
2939 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
2940 wcroot->wc_id, local_relpath, op_depth));
2941 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2945 svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2947 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2955 *status = node_status;
2958 SVN_ERR(convert_to_working_status(status, *status));
2960 repos_location_from_columns(repos_id, revision, repos_relpath,
2961 stmt, 0, 4, 1, result_pool);
2965 *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2969 *changed_date = svn_sqlite__column_int64(stmt, 8);
2973 /* Result may be NULL. */
2974 *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2978 if (node_kind != svn_node_dir)
2980 *depth = svn_depth_unknown;
2984 *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2990 if (node_kind != svn_node_file)
2996 err = svn_sqlite__column_checksum(checksum, stmt, 5,
2999 err = svn_error_createf(
3001 _("The node '%s' has a corrupt checksum value."),
3002 path_for_error_message(wcroot, local_relpath,
3008 if (node_kind != svn_node_symlink)
3011 *target = svn_sqlite__column_text(stmt, 11, result_pool);
3015 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 12);
3019 if (node_status == svn_wc__db_status_normal
3020 || node_status == svn_wc__db_status_incomplete)
3022 SVN_ERR(svn_sqlite__column_properties(props, stmt, 12,
3023 result_pool, scratch_pool));
3025 *props = apr_hash_make(result_pool);
3029 assert(svn_sqlite__column_is_null(stmt, 12));
3036 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3037 _("The node '%s' was not found."),
3038 path_for_error_message(wcroot, local_relpath,
3042 /* Note: given the composition, no need to wrap for tracing. */
3043 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
3046 /* A callback which supplies WCROOTs and LOCAL_RELPATHs. */
3047 typedef svn_error_t *(*svn_wc__db_txn_callback_t)(void *baton,
3048 svn_wc__db_wcroot_t *wcroot,
3049 const char *local_relpath,
3050 apr_pool_t *scratch_pool);
3052 /* Baton for passing args to with_triggers(). */
3053 struct with_triggers_baton_t {
3056 svn_wc__db_txn_callback_t cb_func;
3060 /* Helper for creating SQLite triggers, running the main transaction
3061 callback, and then dropping the triggers. It guarantees that the
3062 triggers will not survive the transaction. This could be used for
3063 any general prefix/postscript statements where the postscript
3064 *must* be executed if the transaction completes.
3066 Implements svn_wc__db_txn_callback_t. */
3067 static svn_error_t *
3068 with_triggers(void *baton,
3069 svn_wc__db_wcroot_t *wcroot,
3070 const char *local_relpath,
3071 apr_pool_t *scratch_pool)
3073 struct with_triggers_baton_t *b = baton;
3077 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, b->create_trigger));
3079 err1 = b->cb_func(b->cb_baton, wcroot, local_relpath, scratch_pool);
3081 err2 = svn_sqlite__exec_statements(wcroot->sdb, b->drop_trigger);
3083 return svn_error_trace(svn_error_compose_create(err1, err2));
3087 /* Prototype for the "work callback" used by with_finalization(). */
3088 typedef svn_error_t * (*work_callback_t)(
3090 svn_wc__db_wcroot_t *wcroot,
3091 svn_cancel_func_t cancel_func,
3093 svn_wc_notify_func2_t notify_func,
3095 apr_pool_t *scratch_pool);
3097 /* Utility function to provide several features, with a guaranteed
3098 finalization (ie. to drop temporary tables).
3100 1) for WCROOT and LOCAL_RELPATH, run TXN_CB(TXN_BATON) within a
3102 2) if (1) is successful and a NOTIFY_FUNC is provided, then run
3103 the "work" step: WORK_CB(WORK_BATON).
3104 3) execute FINALIZE_STMT_IDX no matter what errors may be thrown
3105 from the above two steps.
3107 CANCEL_FUNC, CANCEL_BATON, NOTIFY_FUNC and NOTIFY_BATON are their
3108 typical values. These are passed to the work callback, which typically
3109 provides notification about the work done by TXN_CB. */
3110 static svn_error_t *
3111 with_finalization(svn_wc__db_wcroot_t *wcroot,
3112 const char *local_relpath,
3113 svn_wc__db_txn_callback_t txn_cb,
3115 work_callback_t work_cb,
3117 svn_cancel_func_t cancel_func,
3119 svn_wc_notify_func2_t notify_func,
3121 int finalize_stmt_idx,
3122 apr_pool_t *scratch_pool)
3127 err1 = svn_sqlite__begin_savepoint(wcroot->sdb);
3130 err1 = txn_cb(txn_baton, wcroot, local_relpath, scratch_pool);
3132 err1 = svn_sqlite__finish_savepoint(wcroot->sdb, err1);
3135 if (err1 == NULL && notify_func != NULL)
3137 err2 = work_cb(work_baton, wcroot,
3138 cancel_func, cancel_baton,
3139 notify_func, notify_baton,
3141 err1 = svn_error_compose_create(err1, err2);
3144 err2 = svn_sqlite__exec_statements(wcroot->sdb, finalize_stmt_idx);
3146 return svn_error_trace(svn_error_compose_create(err1, err2));
3150 /* Initialize the baton with appropriate "blank" values. This allows the
3151 insertion function to leave certain columns null. */
3153 blank_ieb(insert_external_baton_t *ieb)
3155 memset(ieb, 0, sizeof(*ieb));
3156 ieb->revision = SVN_INVALID_REVNUM;
3157 ieb->changed_rev = SVN_INVALID_REVNUM;
3158 ieb->repos_id = INVALID_REPOS_ID;
3160 ieb->recorded_peg_revision = SVN_INVALID_REVNUM;
3161 ieb->recorded_revision = SVN_INVALID_REVNUM;
3164 /* Insert the externals row represented by (insert_external_baton_t *) BATON.
3166 * Implements svn_wc__db_txn_callback_t. */
3167 static svn_error_t *
3168 insert_external_node(const insert_external_baton_t *ieb,
3169 svn_wc__db_wcroot_t *wcroot,
3170 const char *local_relpath,
3171 apr_pool_t *scratch_pool)
3173 svn_wc__db_status_t status;
3175 svn_boolean_t update_root;
3176 apr_int64_t repos_id;
3177 svn_sqlite__stmt_t *stmt;
3179 if (ieb->repos_id != INVALID_REPOS_ID)
3180 repos_id = ieb->repos_id;
3182 SVN_ERR(create_repos_id(&repos_id, ieb->repos_root_url, ieb->repos_uuid,
3183 wcroot->sdb, scratch_pool));
3185 /* And there must be no existing BASE node or it must be a file external */
3186 err = svn_wc__db_base_get_info_internal(&status, NULL, NULL, NULL, NULL,
3187 NULL, NULL, NULL, NULL, NULL,
3188 NULL, NULL, NULL, NULL, &update_root,
3189 wcroot, local_relpath,
3190 scratch_pool, scratch_pool);
3193 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3194 return svn_error_trace(err);
3196 svn_error_clear(err);
3198 else if (status == svn_wc__db_status_normal && !update_root)
3199 return svn_error_create(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, NULL);
3201 if (ieb->kind == svn_node_file
3202 || ieb->kind == svn_node_symlink)
3204 struct insert_base_baton_t ibb;
3208 ibb.status = svn_wc__db_status_normal;
3209 ibb.kind = ieb->kind;
3211 ibb.repos_id = repos_id;
3212 ibb.repos_relpath = ieb->repos_relpath;
3213 ibb.revision = ieb->revision;
3215 ibb.props = ieb->props;
3216 ibb.iprops = ieb->iprops;
3217 ibb.changed_rev = ieb->changed_rev;
3218 ibb.changed_date = ieb->changed_date;
3219 ibb.changed_author = ieb->changed_author;
3221 ibb.dav_cache = ieb->dav_cache;
3223 ibb.checksum = ieb->checksum;
3224 ibb.target = ieb->target;
3226 ibb.conflict = ieb->conflict;
3228 ibb.update_actual_props = ieb->update_actual_props;
3229 ibb.new_actual_props = ieb->new_actual_props;
3231 ibb.keep_recorded_info = ieb->keep_recorded_info;
3233 ibb.work_items = ieb->work_items;
3235 ibb.file_external = TRUE;
3237 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
3240 SVN_ERR(add_work_items(wcroot->sdb, ieb->work_items, scratch_pool));
3242 /* The externals table only support presence normal and excluded */
3243 SVN_ERR_ASSERT(ieb->presence == svn_wc__db_status_normal
3244 || ieb->presence == svn_wc__db_status_excluded);
3246 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_EXTERNAL));
3248 SVN_ERR(svn_sqlite__bindf(stmt, "issttsis",
3251 svn_relpath_dirname(local_relpath,
3253 presence_map, ieb->presence,
3254 kind_map, ieb->kind,
3255 ieb->record_ancestor_relpath,
3257 ieb->recorded_repos_relpath));
3259 if (SVN_IS_VALID_REVNUM(ieb->recorded_peg_revision))
3260 SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, ieb->recorded_peg_revision));
3262 if (SVN_IS_VALID_REVNUM(ieb->recorded_revision))
3263 SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, ieb->recorded_revision));
3265 SVN_ERR(svn_sqlite__insert(NULL, stmt));
3267 return SVN_NO_ERROR;
3271 svn_wc__db_external_add_file(svn_wc__db_t *db,
3272 const char *local_abspath,
3273 const char *wri_abspath,
3275 const char *repos_relpath,
3276 const char *repos_root_url,
3277 const char *repos_uuid,
3278 svn_revnum_t revision,
3280 const apr_hash_t *props,
3281 apr_array_header_t *iprops,
3283 svn_revnum_t changed_rev,
3284 apr_time_t changed_date,
3285 const char *changed_author,
3287 const svn_checksum_t *checksum,
3289 const apr_hash_t *dav_cache,
3291 const char *record_ancestor_abspath,
3292 const char *recorded_repos_relpath,
3293 svn_revnum_t recorded_peg_revision,
3294 svn_revnum_t recorded_revision,
3296 svn_boolean_t update_actual_props,
3297 apr_hash_t *new_actual_props,
3299 svn_boolean_t keep_recorded_info,
3300 const svn_skel_t *conflict,
3301 const svn_skel_t *work_items,
3302 apr_pool_t *scratch_pool)
3304 svn_wc__db_wcroot_t *wcroot;
3305 const char *local_relpath;
3306 insert_external_baton_t ieb;
3308 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3311 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3313 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3314 wri_abspath, scratch_pool, scratch_pool));
3315 VERIFY_USABLE_WCROOT(wcroot);
3317 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3318 record_ancestor_abspath));
3320 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3322 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3326 ieb.kind = svn_node_file;
3327 ieb.presence = svn_wc__db_status_normal;
3329 ieb.repos_root_url = repos_root_url;
3330 ieb.repos_uuid = repos_uuid;
3332 ieb.repos_relpath = repos_relpath;
3333 ieb.revision = revision;
3336 ieb.iprops = iprops;
3338 ieb.changed_rev = changed_rev;
3339 ieb.changed_date = changed_date;
3340 ieb.changed_author = changed_author;
3342 ieb.checksum = checksum;
3344 ieb.dav_cache = dav_cache;
3346 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3348 record_ancestor_abspath);
3349 ieb.recorded_repos_relpath = recorded_repos_relpath;
3350 ieb.recorded_peg_revision = recorded_peg_revision;
3351 ieb.recorded_revision = recorded_revision;
3353 ieb.update_actual_props = update_actual_props;
3354 ieb.new_actual_props = new_actual_props;
3356 ieb.keep_recorded_info = keep_recorded_info;
3358 ieb.conflict = conflict;
3359 ieb.work_items = work_items;
3361 SVN_WC__DB_WITH_TXN(
3362 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3365 return SVN_NO_ERROR;
3369 svn_wc__db_external_add_symlink(svn_wc__db_t *db,
3370 const char *local_abspath,
3371 const char *wri_abspath,
3372 const char *repos_relpath,
3373 const char *repos_root_url,
3374 const char *repos_uuid,
3375 svn_revnum_t revision,
3376 const apr_hash_t *props,
3377 svn_revnum_t changed_rev,
3378 apr_time_t changed_date,
3379 const char *changed_author,
3381 const apr_hash_t *dav_cache,
3382 const char *record_ancestor_abspath,
3383 const char *recorded_repos_relpath,
3384 svn_revnum_t recorded_peg_revision,
3385 svn_revnum_t recorded_revision,
3386 svn_boolean_t update_actual_props,
3387 apr_hash_t *new_actual_props,
3388 svn_boolean_t keep_recorded_info,
3389 const svn_skel_t *work_items,
3390 apr_pool_t *scratch_pool)
3392 svn_wc__db_wcroot_t *wcroot;
3393 const char *local_relpath;
3394 insert_external_baton_t ieb;
3396 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3399 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3401 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3402 wri_abspath, scratch_pool, scratch_pool));
3403 VERIFY_USABLE_WCROOT(wcroot);
3405 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3406 record_ancestor_abspath));
3408 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3410 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3414 ieb.kind = svn_node_symlink;
3415 ieb.presence = svn_wc__db_status_normal;
3417 ieb.repos_root_url = repos_root_url;
3418 ieb.repos_uuid = repos_uuid;
3420 ieb.repos_relpath = repos_relpath;
3421 ieb.revision = revision;
3425 ieb.changed_rev = changed_rev;
3426 ieb.changed_date = changed_date;
3427 ieb.changed_author = changed_author;
3429 ieb.target = target;
3431 ieb.dav_cache = dav_cache;
3433 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3435 record_ancestor_abspath);
3436 ieb.recorded_repos_relpath = recorded_repos_relpath;
3437 ieb.recorded_peg_revision = recorded_peg_revision;
3438 ieb.recorded_revision = recorded_revision;
3440 ieb.update_actual_props = update_actual_props;
3441 ieb.new_actual_props = new_actual_props;
3443 ieb.keep_recorded_info = keep_recorded_info;
3445 ieb.work_items = work_items;
3447 SVN_WC__DB_WITH_TXN(
3448 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3451 return SVN_NO_ERROR;
3455 svn_wc__db_external_add_dir(svn_wc__db_t *db,
3456 const char *local_abspath,
3457 const char *wri_abspath,
3458 const char *repos_root_url,
3459 const char *repos_uuid,
3460 const char *record_ancestor_abspath,
3461 const char *recorded_repos_relpath,
3462 svn_revnum_t recorded_peg_revision,
3463 svn_revnum_t recorded_revision,
3464 const svn_skel_t *work_items,
3465 apr_pool_t *scratch_pool)
3467 svn_wc__db_wcroot_t *wcroot;
3468 const char *local_relpath;
3469 insert_external_baton_t ieb;
3471 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3474 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3476 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3477 wri_abspath, scratch_pool, scratch_pool));
3478 VERIFY_USABLE_WCROOT(wcroot);
3480 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3481 record_ancestor_abspath));
3483 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3485 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3489 ieb.kind = svn_node_dir;
3490 ieb.presence = svn_wc__db_status_normal;
3492 ieb.repos_root_url = repos_root_url;
3493 ieb.repos_uuid = repos_uuid;
3495 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3497 record_ancestor_abspath);
3498 ieb.recorded_repos_relpath = recorded_repos_relpath;
3499 ieb.recorded_peg_revision = recorded_peg_revision;
3500 ieb.recorded_revision = recorded_revision;
3502 ieb.work_items = work_items;
3504 SVN_WC__DB_WITH_TXN(
3505 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3508 return SVN_NO_ERROR;
3511 /* The body of svn_wc__db_external_remove(). */
3512 static svn_error_t *
3513 db_external_remove(const svn_skel_t *work_items,
3514 svn_wc__db_wcroot_t *wcroot,
3515 const char *local_relpath,
3516 apr_pool_t *scratch_pool)
3518 svn_sqlite__stmt_t *stmt;
3521 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3522 STMT_DELETE_EXTERNAL));
3523 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3524 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
3527 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3528 _("The node '%s' is not an external."),
3529 path_for_error_message(wcroot, local_relpath,
3532 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
3534 /* ### What about actual? */
3535 return SVN_NO_ERROR;
3539 svn_wc__db_external_remove(svn_wc__db_t *db,
3540 const char *local_abspath,
3541 const char *wri_abspath,
3542 const svn_skel_t *work_items,
3543 apr_pool_t *scratch_pool)
3545 svn_wc__db_wcroot_t *wcroot;
3546 const char *local_relpath;
3548 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3551 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3553 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3554 wri_abspath, scratch_pool, scratch_pool));
3555 VERIFY_USABLE_WCROOT(wcroot);
3557 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3559 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3561 SVN_WC__DB_WITH_TXN(db_external_remove(work_items, wcroot, local_relpath,
3565 return SVN_NO_ERROR;
3569 svn_wc__db_external_read(svn_wc__db_status_t *status,
3570 svn_node_kind_t *kind,
3571 const char **definining_abspath,
3572 const char **repos_root_url,
3573 const char **repos_uuid,
3574 const char **recorded_repos_relpath,
3575 svn_revnum_t *recorded_peg_revision,
3576 svn_revnum_t *recorded_revision,
3578 const char *local_abspath,
3579 const char *wri_abspath,
3580 apr_pool_t *result_pool,
3581 apr_pool_t *scratch_pool)
3583 svn_wc__db_wcroot_t *wcroot;
3584 const char *local_relpath;
3585 svn_sqlite__stmt_t *stmt;
3586 svn_boolean_t have_info;
3587 svn_error_t *err = NULL;
3588 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3591 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3593 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3594 wri_abspath, scratch_pool, scratch_pool));
3595 VERIFY_USABLE_WCROOT(wcroot);
3597 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3599 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3601 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3602 STMT_SELECT_EXTERNAL_INFO));
3603 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3604 SVN_ERR(svn_sqlite__step(&have_info, stmt));
3609 *status = svn_sqlite__column_token(stmt, 0, presence_map);
3612 *kind = svn_sqlite__column_token(stmt, 1, kind_map);
3614 if (definining_abspath)
3616 const char *record_relpath = svn_sqlite__column_text(stmt, 2, NULL);
3618 *definining_abspath = svn_dirent_join(wcroot->abspath,
3619 record_relpath, result_pool);
3622 if (repos_root_url || repos_uuid)
3624 apr_int64_t repos_id;
3626 repos_id = svn_sqlite__column_int64(stmt, 3);
3628 err = svn_error_compose_create(
3630 svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
3635 if (recorded_repos_relpath)
3636 *recorded_repos_relpath = svn_sqlite__column_text(stmt, 4,
3639 if (recorded_peg_revision)
3640 *recorded_peg_revision = svn_sqlite__column_revnum(stmt, 5);
3642 if (recorded_revision)
3643 *recorded_revision = svn_sqlite__column_revnum(stmt, 6);
3647 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3648 _("The node '%s' is not an external."),
3649 svn_dirent_local_style(local_abspath,
3653 return svn_error_trace(
3654 svn_error_compose_create(err, svn_sqlite__reset(stmt)));
3658 svn_wc__db_committable_externals_below(apr_array_header_t **externals,
3660 const char *local_abspath,
3661 svn_boolean_t immediates_only,
3662 apr_pool_t *result_pool,
3663 apr_pool_t *scratch_pool)
3665 svn_wc__db_wcroot_t *wcroot;
3666 svn_sqlite__stmt_t *stmt;
3667 const char *local_relpath;
3668 svn_boolean_t have_row;
3669 svn_wc__committable_external_info_t *info;
3670 svn_node_kind_t db_kind;
3671 apr_array_header_t *result = NULL;
3673 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3675 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3676 local_abspath, scratch_pool, scratch_pool));
3677 VERIFY_USABLE_WCROOT(wcroot);
3679 SVN_ERR(svn_sqlite__get_statement(
3682 ? STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW
3683 : STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW));
3685 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3687 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3690 result = apr_array_make(result_pool, 0,
3691 sizeof(svn_wc__committable_external_info_t *));
3695 info = apr_palloc(result_pool, sizeof(*info));
3697 local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3698 info->local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
3701 db_kind = svn_sqlite__column_token(stmt, 1, kind_map);
3702 SVN_ERR_ASSERT(db_kind == svn_node_file || db_kind == svn_node_dir);
3703 info->kind = db_kind;
3705 info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
3706 info->repos_root_url = svn_sqlite__column_text(stmt, 3, result_pool);
3708 APR_ARRAY_PUSH(result, svn_wc__committable_external_info_t *) = info;
3710 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3713 *externals = result;
3714 return svn_error_trace(svn_sqlite__reset(stmt));
3718 svn_wc__db_externals_defined_below(apr_hash_t **externals,
3720 const char *local_abspath,
3721 apr_pool_t *result_pool,
3722 apr_pool_t *scratch_pool)
3724 svn_wc__db_wcroot_t *wcroot;
3725 svn_sqlite__stmt_t *stmt;
3726 const char *local_relpath;
3727 svn_boolean_t have_row;
3729 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3731 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3732 local_abspath, scratch_pool, scratch_pool));
3733 VERIFY_USABLE_WCROOT(wcroot);
3735 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3736 STMT_SELECT_EXTERNALS_DEFINED));
3738 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3740 *externals = apr_hash_make(result_pool);
3741 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3745 const char *def_local_relpath;
3747 local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3748 def_local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3750 svn_hash_sets(*externals,
3751 svn_dirent_join(wcroot->abspath, local_relpath,
3753 svn_dirent_join(wcroot->abspath, def_local_relpath,
3756 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3759 return svn_error_trace(svn_sqlite__reset(stmt));
3763 svn_wc__db_externals_gather_definitions(apr_hash_t **externals,
3764 apr_hash_t **depths,
3766 const char *local_abspath,
3767 apr_pool_t *result_pool,
3768 apr_pool_t *scratch_pool)
3770 svn_wc__db_wcroot_t *wcroot;
3771 svn_sqlite__stmt_t *stmt;
3772 const char *local_relpath;
3773 svn_boolean_t have_row;
3774 svn_error_t *err = NULL;
3775 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3777 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3779 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3780 local_abspath, scratch_pool, iterpool));
3781 VERIFY_USABLE_WCROOT(wcroot);
3783 *externals = apr_hash_make(result_pool);
3785 *depths = apr_hash_make(result_pool);
3787 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3788 STMT_SELECT_EXTERNAL_PROPERTIES));
3790 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3792 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3796 apr_hash_t *node_props;
3797 const char *external_value;
3799 svn_pool_clear(iterpool);
3800 err = svn_sqlite__column_properties(&node_props, stmt, 0, iterpool,
3806 external_value = svn_prop_get_value(node_props, SVN_PROP_EXTERNALS);
3810 const char *node_abspath;
3811 const char *node_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3813 node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
3816 svn_hash_sets(*externals, node_abspath,
3817 apr_pstrdup(result_pool, external_value));
3822 = svn_sqlite__column_token_null(stmt, 2, depth_map,
3825 svn_hash_sets(*depths, node_abspath,
3826 /* Use static string */
3827 svn_token__to_word(depth_map, depth));
3831 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3834 svn_pool_destroy(iterpool);
3836 return svn_error_trace(svn_error_compose_create(err,
3837 svn_sqlite__reset(stmt)));
3840 /* Copy the ACTUAL data for SRC_RELPATH and tweak it to refer to DST_RELPATH.
3841 The new ACTUAL data won't have any conflicts. */
3842 static svn_error_t *
3843 copy_actual(svn_wc__db_wcroot_t *src_wcroot,
3844 const char *src_relpath,
3845 svn_wc__db_wcroot_t *dst_wcroot,
3846 const char *dst_relpath,
3847 apr_pool_t *scratch_pool)
3849 svn_sqlite__stmt_t *stmt;
3850 svn_boolean_t have_row;
3852 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
3853 STMT_SELECT_ACTUAL_NODE));
3854 SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
3855 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3858 apr_size_t props_size;
3859 const char *changelist;
3860 const char *properties;
3862 /* Skipping conflict data... */
3863 changelist = svn_sqlite__column_text(stmt, 0, scratch_pool);
3864 /* No need to parse the properties when simply copying. */
3865 properties = svn_sqlite__column_blob(stmt, 1, &props_size, scratch_pool);
3867 if (changelist || properties)
3869 SVN_ERR(svn_sqlite__reset(stmt));
3871 SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
3872 STMT_INSERT_ACTUAL_NODE));
3873 SVN_ERR(svn_sqlite__bindf(stmt, "issbs",
3874 dst_wcroot->wc_id, dst_relpath,
3875 svn_relpath_dirname(dst_relpath, scratch_pool),
3876 properties, props_size, changelist));
3877 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3880 SVN_ERR(svn_sqlite__reset(stmt));
3882 return SVN_NO_ERROR;
3885 /* Helper for svn_wc__db_op_copy to handle copying from one db to
3887 static svn_error_t *
3888 cross_db_copy(svn_wc__db_wcroot_t *src_wcroot,
3889 const char *src_relpath,
3890 svn_wc__db_wcroot_t *dst_wcroot,
3891 const char *dst_relpath,
3892 svn_wc__db_status_t dst_status,
3894 int dst_np_op_depth,
3895 svn_node_kind_t kind,
3896 const apr_array_header_t *children,
3897 apr_int64_t copyfrom_id,
3898 const char *copyfrom_relpath,
3899 svn_revnum_t copyfrom_rev,
3900 apr_pool_t *scratch_pool)
3902 insert_working_baton_t iwb;
3903 svn_revnum_t changed_rev;
3904 apr_time_t changed_date;
3905 const char *changed_author;
3906 const svn_checksum_t *checksum;
3910 SVN_ERR_ASSERT(kind == svn_node_file
3911 || kind == svn_node_dir
3914 SVN_ERR(read_info(NULL, NULL, NULL, NULL, NULL,
3915 &changed_rev, &changed_date, &changed_author, &depth,
3916 &checksum, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3917 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3918 src_wcroot, src_relpath, scratch_pool, scratch_pool));
3920 if (dst_status != svn_wc__db_status_not_present
3921 && dst_status != svn_wc__db_status_excluded
3922 && dst_status != svn_wc__db_status_server_excluded)
3924 SVN_ERR(db_read_pristine_props(&props, src_wcroot, src_relpath, FALSE,
3925 scratch_pool, scratch_pool));
3931 iwb.presence = dst_status;
3935 iwb.changed_rev = changed_rev;
3936 iwb.changed_date = changed_date;
3937 iwb.changed_author = changed_author;
3938 iwb.original_repos_id = copyfrom_id;
3939 iwb.original_repos_relpath = copyfrom_relpath;
3940 iwb.original_revnum = copyfrom_rev;
3941 iwb.moved_here = FALSE;
3943 iwb.op_depth = dst_op_depth;
3945 iwb.checksum = checksum;
3946 iwb.children = children;
3949 iwb.not_present_op_depth = dst_np_op_depth;
3951 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath, scratch_pool));
3953 SVN_ERR(copy_actual(src_wcroot, src_relpath,
3954 dst_wcroot, dst_relpath, scratch_pool));
3956 return SVN_NO_ERROR;
3959 /* Helper for scan_deletion_txn. Extracts the moved-to information, if
3960 any, from STMT. Sets *SCAN to FALSE if moved-to was available. */
3961 static svn_error_t *
3962 get_moved_to(const char **moved_to_relpath_p,
3963 const char **moved_to_op_root_relpath_p,
3964 svn_boolean_t *scan,
3965 svn_sqlite__stmt_t *stmt,
3966 const char *current_relpath,
3967 svn_wc__db_wcroot_t *wcroot,
3968 const char *local_relpath,
3969 apr_pool_t *result_pool,
3970 apr_pool_t *scratch_pool)
3972 const char *moved_to_relpath = svn_sqlite__column_text(stmt, 3, NULL);
3974 if (moved_to_relpath)
3976 const char *moved_to_op_root_relpath = moved_to_relpath;
3978 if (strcmp(current_relpath, local_relpath))
3980 /* LOCAL_RELPATH is a child inside the move op-root. */
3981 const char *moved_child_relpath;
3983 /* The CURRENT_RELPATH is the op_root of the delete-half of
3984 * the move. LOCAL_RELPATH is a child that was moved along.
3985 * Compute the child's new location within the move target. */
3986 moved_child_relpath = svn_relpath_skip_ancestor(current_relpath,
3988 SVN_ERR_ASSERT(moved_child_relpath &&
3989 strlen(moved_child_relpath) > 0);
3990 moved_to_relpath = svn_relpath_join(moved_to_op_root_relpath,
3991 moved_child_relpath,
3995 if (moved_to_op_root_relpath && moved_to_op_root_relpath_p)
3996 *moved_to_op_root_relpath_p
3997 = apr_pstrdup(result_pool, moved_to_op_root_relpath);
3999 if (moved_to_relpath && moved_to_relpath_p)
4001 = apr_pstrdup(result_pool, moved_to_relpath);
4006 return SVN_NO_ERROR;
4010 /* The body of svn_wc__db_scan_deletion().
4012 static svn_error_t *
4013 scan_deletion(const char **base_del_relpath,
4014 const char **moved_to_relpath,
4015 const char **work_del_relpath,
4016 const char **moved_to_op_root_relpath,
4017 svn_wc__db_wcroot_t *wcroot,
4018 const char *local_relpath,
4019 apr_pool_t *result_pool,
4020 apr_pool_t *scratch_pool)
4022 const char *current_relpath = local_relpath;
4023 svn_sqlite__stmt_t *stmt;
4024 svn_wc__db_status_t work_presence;
4025 svn_boolean_t have_row, scan, have_base;
4028 /* Initialize all the OUT parameters. */
4029 if (base_del_relpath != NULL)
4030 *base_del_relpath = NULL;
4031 if (moved_to_relpath != NULL)
4032 *moved_to_relpath = NULL;
4033 if (work_del_relpath != NULL)
4034 *work_del_relpath = NULL;
4035 if (moved_to_op_root_relpath != NULL)
4036 *moved_to_op_root_relpath = NULL;
4038 /* If looking for moved-to info then we need to scan every path
4039 until we find it. If not looking for moved-to we only need to
4040 check op-roots and parents of op-roots. */
4041 scan = (moved_to_op_root_relpath || moved_to_relpath);
4043 SVN_ERR(svn_sqlite__get_statement(
4044 &stmt, wcroot->sdb, STMT_SELECT_DELETION_INFO));
4046 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
4047 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4049 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
4050 _("The node '%s' was not found."),
4051 path_for_error_message(wcroot, local_relpath,
4054 work_presence = svn_sqlite__column_token(stmt, 1, presence_map);
4055 have_base = !svn_sqlite__column_is_null(stmt, 0);
4056 if (work_presence != svn_wc__db_status_not_present
4057 && work_presence != svn_wc__db_status_base_deleted)
4058 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
4059 svn_sqlite__reset(stmt),
4060 _("Expected node '%s' to be deleted."),
4061 path_for_error_message(wcroot, local_relpath,
4064 op_depth = svn_sqlite__column_int(stmt, 2);
4066 /* Special case: LOCAL_RELPATH not-present within a WORKING tree, we
4067 treat this as an op-root. At commit time we need to explicitly
4068 delete such nodes otherwise they will be present in the
4070 if (work_presence == svn_wc__db_status_not_present
4071 && work_del_relpath && !*work_del_relpath)
4073 *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
4075 if (!scan && !base_del_relpath)
4077 /* We have all we need, exit early */
4078 SVN_ERR(svn_sqlite__reset(stmt));
4079 return SVN_NO_ERROR;
4087 const char *parent_relpath;
4088 int current_depth = relpath_depth(current_relpath);
4090 /* Step CURRENT_RELPATH to op-root */
4096 err = get_moved_to(moved_to_relpath, moved_to_op_root_relpath,
4097 &scan, stmt, current_relpath,
4098 wcroot, local_relpath,
4099 result_pool, scratch_pool);
4101 && !base_del_relpath
4102 && !work_del_relpath))
4104 /* We have all we need (or an error occurred) */
4105 SVN_ERR(svn_sqlite__reset(stmt));
4106 return svn_error_trace(err);
4110 if (current_depth <= op_depth)
4113 current_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4116 if (scan || current_depth == op_depth)
4118 SVN_ERR(svn_sqlite__reset(stmt));
4119 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
4121 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4122 SVN_ERR_ASSERT(have_row);
4123 have_base = !svn_sqlite__column_is_null(stmt, 0);
4126 SVN_ERR(svn_sqlite__reset(stmt));
4128 /* Now CURRENT_RELPATH is an op-root, have a look at the parent. */
4130 SVN_ERR_ASSERT(current_relpath[0] != '\0'); /* Catch invalid data */
4131 parent_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4132 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4133 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4136 /* No row means no WORKING node which mean we just fell off
4137 the WORKING tree, so CURRENT_RELPATH is the op-root
4138 closest to the wc root. */
4139 if (have_base && base_del_relpath)
4140 *base_del_relpath = apr_pstrdup(result_pool, current_relpath);
4144 /* Still in the WORKING tree so the first time we get here
4145 CURRENT_RELPATH is a delete op-root in the WORKING tree. */
4146 if (work_del_relpath && !*work_del_relpath)
4148 *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
4150 if (!scan && !base_del_relpath)
4151 break; /* We have all we need */
4154 current_relpath = parent_relpath;
4155 op_depth = svn_sqlite__column_int(stmt, 2);
4156 have_base = !svn_sqlite__column_is_null(stmt, 0);
4159 SVN_ERR(svn_sqlite__reset(stmt));
4161 return SVN_NO_ERROR;
4165 svn_wc__db_scan_deletion_internal(
4166 const char **base_del_relpath,
4167 const char **moved_to_relpath,
4168 const char **work_del_relpath,
4169 const char **moved_to_op_root_relpath,
4170 svn_wc__db_wcroot_t *wcroot,
4171 const char *local_relpath,
4172 apr_pool_t *result_pool,
4173 apr_pool_t *scratch_pool)
4175 return svn_error_trace(
4176 scan_deletion(base_del_relpath, moved_to_relpath, work_del_relpath,
4177 moved_to_op_root_relpath,
4178 wcroot, local_relpath,
4179 result_pool, scratch_pool));
4184 svn_wc__db_scan_deletion(const char **base_del_abspath,
4185 const char **moved_to_abspath,
4186 const char **work_del_abspath,
4187 const char **moved_to_op_root_abspath,
4189 const char *local_abspath,
4190 apr_pool_t *result_pool,
4191 apr_pool_t *scratch_pool)
4193 svn_wc__db_wcroot_t *wcroot;
4194 const char *local_relpath;
4195 const char *base_del_relpath, *moved_to_relpath, *work_del_relpath;
4196 const char *moved_to_op_root_relpath;
4198 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4200 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4201 local_abspath, scratch_pool, scratch_pool));
4202 VERIFY_USABLE_WCROOT(wcroot);
4204 SVN_WC__DB_WITH_TXN(
4205 scan_deletion(&base_del_relpath, &moved_to_relpath,
4206 &work_del_relpath, &moved_to_op_root_relpath,
4207 wcroot, local_relpath, result_pool, scratch_pool),
4210 if (base_del_abspath)
4212 *base_del_abspath = (base_del_relpath
4213 ? svn_dirent_join(wcroot->abspath,
4214 base_del_relpath, result_pool)
4217 if (moved_to_abspath)
4219 *moved_to_abspath = (moved_to_relpath
4220 ? svn_dirent_join(wcroot->abspath,
4221 moved_to_relpath, result_pool)
4224 if (work_del_abspath)
4226 *work_del_abspath = (work_del_relpath
4227 ? svn_dirent_join(wcroot->abspath,
4228 work_del_relpath, result_pool)
4231 if (moved_to_op_root_abspath)
4233 *moved_to_op_root_abspath = (moved_to_op_root_relpath
4234 ? svn_dirent_join(wcroot->abspath,
4235 moved_to_op_root_relpath,
4240 return SVN_NO_ERROR;
4244 /* Set *COPYFROM_ID, *COPYFROM_RELPATH, *COPYFROM_REV to the values
4245 appropriate for the copy. Also return *STATUS, *KIND and *HAVE_WORK, *OP_ROOT
4246 since they are available. This is a helper for
4247 svn_wc__db_op_copy. */
4248 static svn_error_t *
4249 get_info_for_copy(apr_int64_t *copyfrom_id,
4250 const char **copyfrom_relpath,
4251 svn_revnum_t *copyfrom_rev,
4252 svn_wc__db_status_t *status,
4253 svn_node_kind_t *kind,
4254 svn_boolean_t *op_root,
4255 svn_wc__db_wcroot_t *src_wcroot,
4256 const char *local_relpath,
4257 svn_wc__db_wcroot_t *dst_wcroot,
4258 apr_pool_t *result_pool,
4259 apr_pool_t *scratch_pool)
4261 const char *repos_relpath;
4262 svn_revnum_t revision;
4263 svn_wc__db_status_t node_status;
4264 apr_int64_t repos_id;
4265 svn_boolean_t is_op_root;
4267 SVN_ERR(read_info(&node_status, kind, &revision, &repos_relpath, &repos_id,
4268 NULL, NULL, NULL, NULL, NULL, NULL, copyfrom_relpath,
4269 copyfrom_id, copyfrom_rev, NULL, NULL, NULL, NULL,
4270 NULL, &is_op_root, NULL, NULL,
4271 NULL /* have_base */,
4272 NULL /* have_more_work */,
4273 NULL /* have_work */,
4274 src_wcroot, local_relpath, result_pool, scratch_pool));
4277 *op_root = is_op_root;
4279 if (node_status == svn_wc__db_status_excluded)
4281 /* The parent cannot be excluded, so look at the parent and then
4282 adjust the relpath */
4283 const char *parent_relpath, *base_name;
4285 svn_dirent_split(&parent_relpath, &base_name, local_relpath,
4287 SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev,
4289 src_wcroot, parent_relpath, dst_wcroot,
4290 scratch_pool, scratch_pool));
4291 if (*copyfrom_relpath)
4292 *copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, base_name,
4295 else if (node_status == svn_wc__db_status_added)
4297 SVN_ERR(scan_addition(&node_status, NULL, NULL, NULL, NULL, NULL, NULL,
4298 NULL, NULL, NULL, src_wcroot, local_relpath,
4299 scratch_pool, scratch_pool));
4301 else if (node_status == svn_wc__db_status_deleted && is_op_root)
4303 const char *base_del_relpath, *work_del_relpath;
4305 SVN_ERR(scan_deletion(&base_del_relpath, NULL,
4307 NULL, src_wcroot, local_relpath,
4308 scratch_pool, scratch_pool));
4309 if (work_del_relpath)
4311 const char *op_root_relpath;
4312 const char *parent_del_relpath = svn_relpath_dirname(work_del_relpath,
4315 /* Similar to, but not the same as, the _scan_addition and
4316 _join above. Can we use get_copyfrom here? */
4317 SVN_ERR(scan_addition(NULL, &op_root_relpath,
4318 NULL, NULL, /* repos_* */
4319 copyfrom_relpath, copyfrom_id, copyfrom_rev,
4321 src_wcroot, parent_del_relpath,
4322 scratch_pool, scratch_pool));
4324 = svn_relpath_join(*copyfrom_relpath,
4325 svn_relpath_skip_ancestor(op_root_relpath,
4329 else if (base_del_relpath)
4331 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, copyfrom_rev,
4333 copyfrom_id, NULL, NULL,
4334 NULL, NULL, NULL, NULL,
4335 NULL, NULL, NULL, NULL,
4336 src_wcroot, local_relpath,
4341 SVN_ERR_MALFUNCTION();
4343 else if (node_status == svn_wc__db_status_deleted)
4345 /* Keep original_* from read_info() to allow seeing the difference
4346 between base-deleted and not present */
4350 *copyfrom_relpath = repos_relpath;
4351 *copyfrom_rev = revision;
4352 *copyfrom_id = repos_id;
4356 *status = node_status;
4358 if (src_wcroot != dst_wcroot && *copyfrom_relpath)
4360 const char *repos_root_url;
4361 const char *repos_uuid;
4363 /* Pass the right repos-id for the destination db. We can't just use
4364 the id of the source database, as this value can change after
4365 relocation (and perhaps also when we start storing multiple
4366 working copies in a single db)! */
4368 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
4369 src_wcroot, *copyfrom_id,
4372 SVN_ERR(create_repos_id(copyfrom_id, repos_root_url, repos_uuid,
4373 dst_wcroot->sdb, scratch_pool));
4376 return SVN_NO_ERROR;
4380 /* Set *OP_DEPTH to the highest op depth of WCROOT:LOCAL_RELPATH. */
4381 static svn_error_t *
4382 op_depth_of(int *op_depth,
4383 svn_wc__db_wcroot_t *wcroot,
4384 const char *local_relpath)
4386 svn_sqlite__stmt_t *stmt;
4387 svn_boolean_t have_row;
4389 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4390 STMT_SELECT_NODE_INFO));
4391 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4392 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4393 SVN_ERR_ASSERT(have_row);
4394 *op_depth = svn_sqlite__column_int(stmt, 0);
4395 SVN_ERR(svn_sqlite__reset(stmt));
4397 return SVN_NO_ERROR;
4401 /* Determine at which OP_DEPTH a copy of COPYFROM_REPOS_ID, COPYFROM_RELPATH at
4402 revision COPYFROM_REVISION should be inserted as LOCAL_RELPATH. Do this
4403 by checking if this would be a direct child of a copy of its parent
4404 directory. If it is then set *OP_DEPTH to the op_depth of its parent.
4406 If the node is not a direct copy at the same revision of the parent
4407 *NP_OP_DEPTH will be set to the op_depth of the parent when a not-present
4408 node should be inserted at this op_depth. This will be the case when the
4409 parent already defined an incomplete child with the same name. Otherwise
4410 *NP_OP_DEPTH will be set to -1.
4412 If the parent node is not the parent of the to be copied node, then
4413 *OP_DEPTH will be set to the proper op_depth for a new operation root.
4415 Set *PARENT_OP_DEPTH to the op_depth of the parent.
4418 static svn_error_t *
4419 op_depth_for_copy(int *op_depth,
4421 int *parent_op_depth,
4422 apr_int64_t copyfrom_repos_id,
4423 const char *copyfrom_relpath,
4424 svn_revnum_t copyfrom_revision,
4425 svn_wc__db_wcroot_t *wcroot,
4426 const char *local_relpath,
4427 apr_pool_t *scratch_pool)
4429 const char *parent_relpath, *name;
4430 svn_sqlite__stmt_t *stmt;
4431 svn_boolean_t have_row;
4432 int incomplete_op_depth = -1;
4433 int min_op_depth = 1; /* Never touch BASE */
4435 *op_depth = relpath_depth(local_relpath);
4438 svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool);
4439 *parent_op_depth = relpath_depth(parent_relpath);
4441 if (!copyfrom_relpath)
4442 return SVN_NO_ERROR;
4444 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4445 STMT_SELECT_WORKING_NODE));
4446 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4447 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4450 svn_wc__db_status_t status = svn_sqlite__column_token(stmt, 1,
4453 min_op_depth = svn_sqlite__column_int(stmt, 0);
4454 if (status == svn_wc__db_status_incomplete)
4455 incomplete_op_depth = min_op_depth;
4457 SVN_ERR(svn_sqlite__reset(stmt));
4459 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4460 STMT_SELECT_WORKING_NODE));
4461 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4462 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4465 svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 1,
4468 *parent_op_depth = svn_sqlite__column_int(stmt, 0);
4469 if (*parent_op_depth < min_op_depth)
4471 /* We want to create a copy; not overwrite the lower layers */
4472 SVN_ERR(svn_sqlite__reset(stmt));
4473 return SVN_NO_ERROR;
4476 /* You can only add children below a node that exists.
4477 In WORKING that must be status added, which is represented
4478 as presence normal */
4479 SVN_ERR_ASSERT(presence == svn_wc__db_status_normal);
4481 if ((incomplete_op_depth < 0)
4482 || (incomplete_op_depth == *parent_op_depth))
4484 apr_int64_t parent_copyfrom_repos_id
4485 = svn_sqlite__column_int64(stmt, 10);
4486 const char *parent_copyfrom_relpath
4487 = svn_sqlite__column_text(stmt, 11, NULL);
4488 svn_revnum_t parent_copyfrom_revision
4489 = svn_sqlite__column_revnum(stmt, 12);
4491 if (parent_copyfrom_repos_id == copyfrom_repos_id)
4493 if (copyfrom_revision == parent_copyfrom_revision
4494 && !strcmp(copyfrom_relpath,
4495 svn_relpath_join(parent_copyfrom_relpath, name,
4497 *op_depth = *parent_op_depth;
4498 else if (incomplete_op_depth > 0)
4499 *np_op_depth = incomplete_op_depth;
4503 SVN_ERR(svn_sqlite__reset(stmt));
4505 return SVN_NO_ERROR;
4509 /* Like svn_wc__db_op_copy(), but with WCROOT+LOCAL_RELPATH
4510 * instead of DB+LOCAL_ABSPATH. A non-zero MOVE_OP_DEPTH implies that the
4511 * copy operation is part of a move, and indicates the op-depth of the
4512 * move destination op-root. */
4513 static svn_error_t *
4514 db_op_copy(svn_wc__db_wcroot_t *src_wcroot,
4515 const char *src_relpath,
4516 svn_wc__db_wcroot_t *dst_wcroot,
4517 const char *dst_relpath,
4518 const svn_skel_t *work_items,
4520 apr_pool_t *scratch_pool)
4522 const char *copyfrom_relpath;
4523 svn_revnum_t copyfrom_rev;
4524 svn_wc__db_status_t status;
4525 svn_wc__db_status_t dst_presence;
4526 svn_boolean_t op_root;
4527 apr_int64_t copyfrom_id;
4529 int dst_np_op_depth;
4530 int dst_parent_op_depth;
4531 svn_node_kind_t kind;
4532 const apr_array_header_t *children;
4534 SVN_ERR(get_info_for_copy(©from_id, ©from_relpath, ©from_rev,
4535 &status, &kind, &op_root,
4536 src_wcroot, src_relpath, dst_wcroot,
4537 scratch_pool, scratch_pool));
4539 SVN_ERR(op_depth_for_copy(&dst_op_depth, &dst_np_op_depth,
4540 &dst_parent_op_depth,
4541 copyfrom_id, copyfrom_relpath, copyfrom_rev,
4542 dst_wcroot, dst_relpath, scratch_pool));
4544 SVN_ERR_ASSERT(kind == svn_node_file || kind == svn_node_dir);
4546 /* ### New status, not finished, see notes/wc-ng/copying */
4549 case svn_wc__db_status_normal:
4550 case svn_wc__db_status_added:
4551 case svn_wc__db_status_moved_here:
4552 case svn_wc__db_status_copied:
4553 dst_presence = svn_wc__db_status_normal;
4555 case svn_wc__db_status_deleted:
4558 /* If the lower layer is already shadowcopied we can skip adding
4559 a not present node. */
4561 svn_wc__db_status_t dst_status;
4563 err = read_info(&dst_status, NULL, NULL, NULL, NULL, NULL, NULL,
4564 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4565 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4566 dst_wcroot, dst_relpath, scratch_pool, scratch_pool);
4570 if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
4571 svn_error_clear(err);
4573 return svn_error_trace(err);
4575 else if (dst_status == svn_wc__db_status_deleted)
4577 /* Node is already deleted; skip the NODES work, but do
4578 install wq items if requested */
4579 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4581 return SVN_NO_ERROR;
4586 /* This node is either a not-present node (which should be copied), or
4587 a base-delete of some lower layer (which shouldn't).
4588 Subversion <= 1.7 always added a not-present node here, which is
4589 safe (as it postpones the hard work until commit time and then we
4590 ask the repository), but it breaks some move scenarios.
4593 if (! copyfrom_relpath)
4595 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4597 return SVN_NO_ERROR;
4600 /* Fall through. Install not present node */
4602 case svn_wc__db_status_not_present:
4603 case svn_wc__db_status_excluded:
4604 /* These presence values should not create a new op depth */
4605 if (dst_np_op_depth > 0)
4607 dst_op_depth = dst_np_op_depth;
4608 dst_np_op_depth = -1;
4610 if (status == svn_wc__db_status_excluded)
4611 dst_presence = svn_wc__db_status_excluded;
4613 dst_presence = svn_wc__db_status_not_present;
4615 case svn_wc__db_status_server_excluded:
4616 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4617 _("Cannot copy '%s' excluded by server"),
4618 path_for_error_message(src_wcroot,
4622 /* Perhaps we should allow incomplete to incomplete? We can't
4623 avoid incomplete working nodes as one step in copying a
4624 directory is to add incomplete children. */
4625 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4626 _("Cannot handle status of '%s'"),
4627 path_for_error_message(src_wcroot,
4632 if (kind == svn_node_dir)
4636 SVN_ERR(op_depth_of(&src_op_depth, src_wcroot, src_relpath));
4637 SVN_ERR(gather_children(&children, src_wcroot, src_relpath,
4638 STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth,
4639 scratch_pool, scratch_pool));
4644 if (src_wcroot == dst_wcroot)
4646 svn_sqlite__stmt_t *stmt;
4647 const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
4650 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
4651 STMT_INSERT_WORKING_NODE_COPY_FROM));
4653 SVN_ERR(svn_sqlite__bindf(stmt, "issdst",
4654 src_wcroot->wc_id, src_relpath,
4658 presence_map, dst_presence));
4660 if (move_op_depth > 0)
4662 if (relpath_depth(dst_relpath) == move_op_depth)
4664 /* We're moving the root of the move operation.
4666 * When an added node or the op-root of a copy is moved,
4667 * there is no 'moved-from' corresponding to the moved-here
4668 * node. So the net effect is the same as copy+delete.
4669 * Perform a normal copy operation in these cases. */
4670 if (!(status == svn_wc__db_status_added ||
4671 (status == svn_wc__db_status_copied && op_root)))
4672 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4676 svn_sqlite__stmt_t *info_stmt;
4677 svn_boolean_t have_row;
4679 /* We're moving a child along with the root of the move.
4681 * Set moved-here depending on dst_parent, propagating the
4682 * above decision to moved-along children at the same op_depth.
4683 * We can't use scan_addition() to detect moved-here because
4684 * the delete-half of the move might not yet exist. */
4685 SVN_ERR(svn_sqlite__get_statement(&info_stmt, dst_wcroot->sdb,
4686 STMT_SELECT_NODE_INFO));
4687 SVN_ERR(svn_sqlite__bindf(info_stmt, "is", dst_wcroot->wc_id,
4688 dst_parent_relpath));
4689 SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4690 SVN_ERR_ASSERT(have_row);
4691 if (svn_sqlite__column_boolean(info_stmt, 15) &&
4692 dst_op_depth == dst_parent_op_depth)
4694 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4695 SVN_ERR(svn_sqlite__reset(info_stmt));
4699 SVN_ERR(svn_sqlite__reset(info_stmt));
4701 /* If the child has been moved into the tree we're moving,
4702 * keep its moved-here bit set. */
4703 SVN_ERR(svn_sqlite__get_statement(&info_stmt,
4705 STMT_SELECT_NODE_INFO));
4706 SVN_ERR(svn_sqlite__bindf(info_stmt, "is",
4707 dst_wcroot->wc_id, src_relpath));
4708 SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4709 SVN_ERR_ASSERT(have_row);
4710 if (svn_sqlite__column_boolean(info_stmt, 15))
4711 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4712 SVN_ERR(svn_sqlite__reset(info_stmt));
4717 SVN_ERR(svn_sqlite__step_done(stmt));
4719 /* ### Copying changelist is OK for a move but what about a copy? */
4720 SVN_ERR(copy_actual(src_wcroot, src_relpath,
4721 dst_wcroot, dst_relpath, scratch_pool));
4723 if (dst_np_op_depth > 0)
4725 /* We introduce a not-present node at the parent's op_depth to
4726 properly start a new op-depth at our own op_depth. This marks
4727 us as an op_root for commit and allows reverting just this
4730 SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
4732 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
4733 src_wcroot->wc_id, dst_relpath,
4734 dst_np_op_depth, dst_parent_relpath,
4735 copyfrom_id, copyfrom_relpath,
4738 svn_wc__db_status_not_present,
4742 SVN_ERR(svn_sqlite__step_done(stmt));
4744 /* Insert incomplete children, if relevant.
4745 The children are part of the same op and so have the same op_depth.
4746 (The only time we'd want a different depth is during a recursive
4747 simple add, but we never insert children here during a simple add.) */
4748 if (kind == svn_node_dir
4749 && dst_presence == svn_wc__db_status_normal)
4750 SVN_ERR(insert_incomplete_children(
4763 SVN_ERR(cross_db_copy(src_wcroot, src_relpath, dst_wcroot,
4764 dst_relpath, dst_presence, dst_op_depth,
4765 dst_np_op_depth, kind,
4766 children, copyfrom_id, copyfrom_relpath,
4767 copyfrom_rev, scratch_pool));
4770 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items, scratch_pool));
4772 return SVN_NO_ERROR;
4775 /* Baton for passing args to op_copy_txn(). */
4776 struct op_copy_baton
4778 svn_wc__db_wcroot_t *src_wcroot;
4779 const char *src_relpath;
4781 svn_wc__db_wcroot_t *dst_wcroot;
4782 const char *dst_relpath;
4784 const svn_skel_t *work_items;
4786 svn_boolean_t is_move;
4787 const char *dst_op_root_relpath;
4790 /* Helper for svn_wc__db_op_copy(). */
4791 static svn_error_t *
4792 op_copy_txn(svn_wc__db_wcroot_t *wcroot,
4793 struct op_copy_baton *ocb,
4794 apr_pool_t *scratch_pool)
4798 if (wcroot != ocb->dst_wcroot)
4800 /* Source and destination databases differ; so also start a lock
4801 in the destination database, by calling ourself in an extra lock. */
4803 SVN_WC__DB_WITH_TXN(op_copy_txn(ocb->dst_wcroot, ocb, scratch_pool),
4806 return SVN_NO_ERROR;
4809 /* From this point we can assume a lock in the src and dst databases */
4812 move_op_depth = relpath_depth(ocb->dst_op_root_relpath);
4816 SVN_ERR(db_op_copy(ocb->src_wcroot, ocb->src_relpath,
4817 ocb->dst_wcroot, ocb->dst_relpath,
4818 ocb->work_items, move_op_depth, scratch_pool));
4820 return SVN_NO_ERROR;
4824 svn_wc__db_op_copy(svn_wc__db_t *db,
4825 const char *src_abspath,
4826 const char *dst_abspath,
4827 const char *dst_op_root_abspath,
4828 svn_boolean_t is_move,
4829 const svn_skel_t *work_items,
4830 apr_pool_t *scratch_pool)
4832 struct op_copy_baton ocb = {0};
4834 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
4835 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
4836 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_op_root_abspath));
4838 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
4839 &ocb.src_relpath, db,
4841 scratch_pool, scratch_pool));
4842 VERIFY_USABLE_WCROOT(ocb.src_wcroot);
4844 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
4847 scratch_pool, scratch_pool));
4848 VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
4850 ocb.work_items = work_items;
4851 ocb.is_move = is_move;
4852 ocb.dst_op_root_relpath = svn_dirent_skip_ancestor(ocb.dst_wcroot->abspath,
4853 dst_op_root_abspath);
4855 /* Call with the sdb in src_wcroot. It might call itself again to
4856 also obtain a lock in dst_wcroot */
4857 SVN_WC__DB_WITH_TXN(op_copy_txn(ocb.src_wcroot, &ocb, scratch_pool),
4860 return SVN_NO_ERROR;
4863 /* Remove unneeded actual nodes for svn_wc__db_op_copy_layer_internal */
4864 static svn_error_t *
4865 clear_or_remove_actual(svn_wc__db_wcroot_t *wcroot,
4866 const char *local_relpath,
4868 apr_pool_t *scratch_pool)
4870 svn_sqlite__stmt_t *stmt;
4871 svn_boolean_t have_row, shadowed;
4872 svn_boolean_t keep_conflict = FALSE;
4874 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4875 STMT_SELECT_NODE_INFO));
4877 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4878 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4882 svn_wc__db_status_t presence;
4884 shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth);
4885 presence = svn_sqlite__column_token(stmt, 3, presence_map);
4887 if (shadowed && presence == svn_wc__db_status_base_deleted)
4889 keep_conflict = TRUE;
4890 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4893 shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth);
4901 SVN_ERR(svn_sqlite__reset(stmt));
4903 return SVN_NO_ERROR;
4907 /* We don't want to accidentally remove delete-delete conflicts */
4908 SVN_ERR(svn_sqlite__get_statement(
4910 STMT_CLEAR_ACTUAL_NODE_LEAVING_CONFLICT));
4911 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4912 SVN_ERR(svn_sqlite__step_done(stmt));
4913 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4914 STMT_DELETE_ACTUAL_EMPTY));
4915 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4916 SVN_ERR(svn_sqlite__step_done(stmt));
4920 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4921 STMT_DELETE_ACTUAL_NODE));
4922 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4923 SVN_ERR(svn_sqlite__step_done(stmt));
4926 return SVN_NO_ERROR;
4930 svn_wc__db_op_copy_layer_internal(svn_wc__db_wcroot_t *wcroot,
4931 const char *src_op_relpath,
4933 const char *dst_op_relpath,
4934 svn_skel_t *conflict,
4935 svn_skel_t *work_items,
4936 apr_pool_t *scratch_pool)
4938 svn_sqlite__stmt_t *stmt, *stmt2;
4939 svn_boolean_t have_row;
4940 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4941 int dst_op_depth = relpath_depth(dst_op_relpath);
4942 svn_boolean_t locked;
4943 svn_error_t *err = NULL;
4945 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, dst_op_relpath,
4946 FALSE, scratch_pool));
4949 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
4950 _("No write-lock in '%s'"),
4951 path_for_error_message(wcroot, dst_op_relpath,
4954 SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
4955 STMT_COPY_NODE_MOVE));
4957 /* Replace entire subtree at one op-depth. */
4958 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4959 STMT_SELECT_LAYER_FOR_REPLACE));
4960 SVN_ERR(svn_sqlite__bindf(stmt, "isdsd", wcroot->wc_id,
4961 src_op_relpath, src_op_depth,
4962 dst_op_relpath, dst_op_depth));
4963 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4966 const char *src_relpath;
4967 const char *dst_relpath;
4969 svn_pool_clear(iterpool);
4971 src_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
4972 dst_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
4974 err = svn_sqlite__bindf(stmt2, "isdsds", wcroot->wc_id,
4975 src_relpath, src_op_depth,
4976 dst_relpath, dst_op_depth,
4977 svn_relpath_dirname(dst_relpath, iterpool));
4979 err = svn_sqlite__step_done(stmt2);
4981 /* stmt2 is reset (never modified or by step_done) */
4986 /* The node can't be deleted where it is added, so extension of
4987 an existing shadowing is only interesting 2 levels deep. */
4988 if (relpath_depth(dst_relpath) > (dst_op_depth+1))
4990 svn_boolean_t exists = !svn_sqlite__column_is_null(stmt, 3);
4994 svn_wc__db_status_t presence;
4996 presence = svn_sqlite__column_token(stmt, 3, presence_map);
4998 if (presence != svn_wc__db_status_normal)
5004 svn_node_kind_t kind = svn_sqlite__column_token(stmt, 1, kind_map);
5006 err = db_extend_parent_delete(wcroot, dst_relpath,
5007 kind, dst_op_depth, iterpool);
5014 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5017 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
5019 /* And now remove the records that are no longer needed */
5020 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5021 STMT_SELECT_NO_LONGER_MOVED_RV));
5022 SVN_ERR(svn_sqlite__bindf(stmt, "isdsd", wcroot->wc_id,
5023 dst_op_relpath, dst_op_depth,
5024 src_op_relpath, src_op_depth));
5025 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5028 const char *dst_relpath;
5029 svn_wc__db_status_t shadowed_presence;
5031 svn_pool_clear(iterpool);
5033 dst_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
5035 if (!svn_sqlite__column_is_null(stmt, 2))
5036 shadowed_presence = svn_sqlite__column_token(stmt, 2, presence_map);
5038 shadowed_presence = svn_wc__db_status_not_present;
5040 if (shadowed_presence != svn_wc__db_status_normal
5041 && shadowed_presence != svn_wc__db_status_incomplete)
5043 err = svn_sqlite__get_statement(&stmt2, wcroot->sdb,
5048 err =svn_sqlite__get_statement(&stmt2, wcroot->sdb,
5049 STMT_REPLACE_WITH_BASE_DELETED);
5053 err = svn_sqlite__bindf(stmt2, "isd", wcroot->wc_id, dst_relpath,
5057 err = svn_sqlite__step_done(stmt2);
5059 /* stmt2 is reset (never modified or by step_done) */
5063 /* Delete ACTUAL information about this node that we just deleted */
5064 err = clear_or_remove_actual(wcroot, dst_relpath, dst_op_depth,
5070 /* Retract base-delete for the node itself */
5071 err = db_retract_parent_delete(wcroot, dst_relpath, dst_op_depth,
5077 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5079 svn_pool_destroy(iterpool);
5081 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
5083 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5086 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, dst_op_relpath /* ## */,
5087 conflict, scratch_pool));
5089 return SVN_NO_ERROR;
5092 /* The txn body of svn_wc__db_op_handle_move_back */
5093 static svn_error_t *
5094 handle_move_back(svn_boolean_t *moved_back,
5095 svn_wc__db_wcroot_t *wcroot,
5096 const char *local_relpath,
5097 const char *moved_from_relpath,
5098 const svn_skel_t *work_items,
5099 apr_pool_t *scratch_pool)
5101 svn_sqlite__stmt_t *stmt;
5102 svn_wc__db_status_t status;
5103 svn_boolean_t op_root;
5104 svn_boolean_t have_more_work;
5105 int from_op_depth = 0;
5106 svn_boolean_t have_row;
5107 svn_boolean_t different = FALSE;
5109 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5111 SVN_ERR(svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL, NULL,
5112 NULL, NULL, NULL, NULL, NULL, NULL,
5113 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5114 &op_root, NULL, NULL, NULL,
5115 &have_more_work, NULL,
5116 wcroot, local_relpath,
5117 scratch_pool, scratch_pool));
5119 if (status != svn_wc__db_status_added || !op_root)
5120 return SVN_NO_ERROR;
5122 /* We have two cases here: BASE-move-back and WORKING-move-back */
5124 SVN_ERR(op_depth_of(&from_op_depth, wcroot,
5125 svn_relpath_dirname(local_relpath, scratch_pool)));
5129 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5130 STMT_SELECT_MOVED_BACK));
5132 SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id,
5135 relpath_depth(local_relpath)));
5137 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5139 SVN_ERR_ASSERT(have_row); /* We checked that the node is an op-root */
5142 svn_boolean_t moved_here = svn_sqlite__column_boolean(stmt, 9);
5143 const char *moved_to = svn_sqlite__column_text(stmt, 10, NULL);
5147 || strcmp(moved_to, moved_from_relpath))
5156 svn_wc__db_status_t upper_status;
5157 svn_wc__db_status_t lower_status;
5159 upper_status = svn_sqlite__column_token(stmt, 1, presence_map);
5161 if (svn_sqlite__column_is_null(stmt, 5))
5163 /* No lower layer replaced. */
5164 if (upper_status != svn_wc__db_status_not_present)
5172 lower_status = svn_sqlite__column_token(stmt, 5, presence_map);
5174 if (upper_status != lower_status)
5180 if (upper_status == svn_wc__db_status_not_present
5181 || upper_status == svn_wc__db_status_excluded)
5183 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5184 continue; /* Nothing to check */
5186 else if (upper_status != svn_wc__db_status_normal)
5188 /* Not a normal move. Mixed revision move? */
5194 const char *upper_repos_relpath;
5195 const char *lower_repos_relpath;
5197 upper_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL);
5198 lower_repos_relpath = svn_sqlite__column_text(stmt, 7, NULL);
5200 if (! upper_repos_relpath
5201 || strcmp(upper_repos_relpath, lower_repos_relpath))
5209 svn_revnum_t upper_rev;
5210 svn_revnum_t lower_rev;
5212 upper_rev = svn_sqlite__column_revnum(stmt, 4);
5213 lower_rev = svn_sqlite__column_revnum(stmt, 8);
5215 if (upper_rev != lower_rev)
5223 apr_int64_t upper_repos_id;
5224 apr_int64_t lower_repos_id;
5226 upper_repos_id = svn_sqlite__column_int64(stmt, 2);
5227 lower_repos_id = svn_sqlite__column_int64(stmt, 6);
5229 if (upper_repos_id != lower_repos_id)
5236 /* Check moved_here? */
5238 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5240 SVN_ERR(svn_sqlite__reset(stmt));
5244 /* Ok, we can now safely remove this complete move, because we
5245 determined that it 100% matches the layer below it. */
5247 /* ### We could copy the recorded timestamps from the higher to the
5248 lower layer in an attempt to improve status performance, but
5249 generally these values should be the same anyway as it was
5251 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5252 STMT_DELETE_WORKING_OP_DEPTH));
5254 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
5256 relpath_depth(local_relpath)));
5258 SVN_ERR(svn_sqlite__step_done(stmt));
5264 return SVN_NO_ERROR;
5268 svn_wc__db_op_handle_move_back(svn_boolean_t *moved_back,
5270 const char *local_abspath,
5271 const char *moved_from_abspath,
5272 const svn_skel_t *work_items,
5273 apr_pool_t *scratch_pool)
5275 svn_wc__db_wcroot_t *wcroot;
5276 const char *local_relpath;
5277 const char *moved_from_relpath;
5278 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5280 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5282 scratch_pool, scratch_pool));
5283 VERIFY_USABLE_WCROOT(wcroot);
5286 *moved_back = FALSE;
5288 moved_from_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
5289 moved_from_abspath);
5291 if (! local_relpath[0]
5292 || !moved_from_relpath)
5294 /* WC-Roots can't be moved */
5295 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5296 return SVN_NO_ERROR;
5299 SVN_WC__DB_WITH_TXN(handle_move_back(moved_back, wcroot, local_relpath,
5300 moved_from_relpath, work_items,
5304 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5307 return SVN_NO_ERROR;
5311 /* The recursive implementation of svn_wc__db_op_copy_shadowed_layer.
5313 * A non-zero MOVE_OP_DEPTH implies that the copy operation is part of
5314 * a move, and indicates the op-depth of the move destination op-root. */
5315 static svn_error_t *
5316 db_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot,
5317 const char *src_relpath,
5319 svn_wc__db_wcroot_t *dst_wcroot,
5320 const char *dst_relpath,
5323 apr_int64_t repos_id,
5324 const char *repos_relpath,
5325 svn_revnum_t revision,
5327 apr_pool_t *scratch_pool)
5329 const apr_array_header_t *children;
5330 apr_pool_t *iterpool;
5331 svn_wc__db_status_t status;
5332 svn_node_kind_t kind;
5333 svn_revnum_t node_revision;
5334 const char *node_repos_relpath;
5335 apr_int64_t node_repos_id;
5336 svn_sqlite__stmt_t *stmt;
5337 svn_wc__db_status_t dst_presence;
5342 err = svn_wc__db_depth_get_info(&status, &kind, &node_revision,
5343 &node_repos_relpath, &node_repos_id,
5344 NULL, NULL, NULL, NULL, NULL, NULL,
5346 src_wcroot, src_relpath, src_op_depth,
5347 scratch_pool, scratch_pool);
5351 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5352 return svn_error_trace(err);
5354 svn_error_clear(err);
5355 return SVN_NO_ERROR; /* There is no shadowed node at src_op_depth */
5359 if (src_op_depth == 0)
5361 /* If the node is switched or has a different revision then its parent
5362 we shouldn't copy it. (We can't as we would have to insert it at
5363 an unshadowed depth) */
5364 if (status == svn_wc__db_status_not_present
5365 || status == svn_wc__db_status_excluded
5366 || status == svn_wc__db_status_server_excluded
5367 || node_revision != revision
5368 || node_repos_id != repos_id
5369 || strcmp(node_repos_relpath, repos_relpath))
5371 /* Add a not-present node in the destination wcroot */
5372 struct insert_working_baton_t iwb;
5373 const char *repos_root_url;
5374 const char *repos_uuid;
5376 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
5377 src_wcroot, node_repos_id,
5380 SVN_ERR(create_repos_id(&node_repos_id, repos_root_url, repos_uuid,
5381 dst_wcroot->sdb, scratch_pool));
5385 iwb.op_depth = dst_op_depth;
5386 if (status != svn_wc__db_status_excluded)
5387 iwb.presence = svn_wc__db_status_not_present;
5389 iwb.presence = svn_wc__db_status_excluded;
5393 iwb.original_repos_id = node_repos_id;
5394 iwb.original_revnum = node_revision;
5395 iwb.original_repos_relpath = node_repos_relpath;
5397 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5400 return SVN_NO_ERROR;
5404 iterpool = svn_pool_create(scratch_pool);
5408 case svn_wc__db_status_normal:
5409 case svn_wc__db_status_added:
5410 case svn_wc__db_status_moved_here:
5411 case svn_wc__db_status_copied:
5412 dst_presence = svn_wc__db_status_normal;
5414 case svn_wc__db_status_deleted:
5415 case svn_wc__db_status_not_present:
5416 dst_presence = svn_wc__db_status_not_present;
5418 case svn_wc__db_status_excluded:
5419 dst_presence = svn_wc__db_status_excluded;
5421 case svn_wc__db_status_server_excluded:
5422 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5423 _("Cannot copy '%s' excluded by server"),
5424 path_for_error_message(src_wcroot,
5428 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5429 _("Cannot handle status of '%s'"),
5430 path_for_error_message(src_wcroot,
5435 if (dst_presence == svn_wc__db_status_normal
5436 && src_wcroot == dst_wcroot) /* ### Remove limitation */
5438 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
5439 STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH));
5441 SVN_ERR(svn_sqlite__bindf(stmt, "issdstd",
5442 src_wcroot->wc_id, src_relpath,
5445 svn_relpath_dirname(dst_relpath, iterpool),
5446 presence_map, dst_presence,
5450 if (dst_op_depth == move_op_depth)
5451 SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
5453 SVN_ERR(svn_sqlite__step_done(stmt));
5456 /* And mark it deleted to allow proper shadowing */
5457 struct insert_working_baton_t iwb;
5461 iwb.op_depth = del_op_depth;
5462 iwb.presence = svn_wc__db_status_base_deleted;
5466 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5472 struct insert_working_baton_t iwb;
5473 if (dst_presence == svn_wc__db_status_normal) /* Fallback for multi-db */
5474 dst_presence = svn_wc__db_status_not_present;
5476 /* And mark it deleted to allow proper shadowing */
5480 iwb.op_depth = dst_op_depth;
5481 iwb.presence = dst_presence;
5484 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5488 if (dst_presence == svn_wc__db_status_not_present)
5490 /* Don't create descendants of a not present node! */
5492 /* This code is currently still triggered by copying deleted nodes
5493 between separate working copies. See ### comment above. */
5495 svn_pool_destroy(iterpool);
5496 return SVN_NO_ERROR;
5499 SVN_ERR(gather_children(&children, src_wcroot, src_relpath,
5500 STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth,
5501 scratch_pool, iterpool));
5503 for (i = 0; i < children->nelts; i++)
5505 const char *name = APR_ARRAY_IDX(children, i, const char *);
5506 const char *child_src_relpath;
5507 const char *child_dst_relpath;
5508 const char *child_repos_relpath = NULL;
5510 svn_pool_clear(iterpool);
5511 child_src_relpath = svn_relpath_join(src_relpath, name, iterpool);
5512 child_dst_relpath = svn_relpath_join(dst_relpath, name, iterpool);
5515 child_repos_relpath = svn_relpath_join(repos_relpath, name, iterpool);
5517 SVN_ERR(db_op_copy_shadowed_layer(
5518 src_wcroot, child_src_relpath, src_op_depth,
5519 dst_wcroot, child_dst_relpath, dst_op_depth,
5521 repos_id, child_repos_relpath, revision,
5522 move_op_depth, scratch_pool));
5525 svn_pool_destroy(iterpool);
5527 return SVN_NO_ERROR;
5530 /* Helper for svn_wc__db_op_copy_shadowed_layer(). */
5531 static svn_error_t *
5532 op_copy_shadowed_layer_txn(svn_wc__db_wcroot_t *wcroot,
5533 struct op_copy_baton *ocb,
5534 apr_pool_t *scratch_pool)
5536 const char *src_parent_relpath;
5537 const char *dst_parent_relpath;
5541 const char *repos_relpath = NULL;
5542 apr_int64_t repos_id = INVALID_REPOS_ID;
5543 svn_revnum_t revision = SVN_INVALID_REVNUM;
5545 if (wcroot != ocb->dst_wcroot)
5547 /* Source and destination databases differ; so also start a lock
5548 in the destination database, by calling ourself in an extra lock. */
5550 SVN_WC__DB_WITH_TXN(op_copy_shadowed_layer_txn(ocb->dst_wcroot, ocb,
5554 return SVN_NO_ERROR;
5557 /* From this point we can assume a lock in the src and dst databases */
5560 /* src_relpath and dst_relpath can't be wcroot as we need their parents */
5561 SVN_ERR_ASSERT(*ocb->src_relpath && *ocb->dst_relpath);
5563 src_parent_relpath = svn_relpath_dirname(ocb->src_relpath, scratch_pool);
5564 dst_parent_relpath = svn_relpath_dirname(ocb->dst_relpath, scratch_pool);
5566 /* src_parent must be status normal or added; get its op-depth */
5567 SVN_ERR(op_depth_of(&src_op_depth, ocb->src_wcroot, src_parent_relpath));
5569 /* dst_parent must be status added; get its op-depth */
5570 SVN_ERR(op_depth_of(&dst_op_depth, ocb->dst_wcroot, dst_parent_relpath));
5572 del_op_depth = relpath_depth(ocb->dst_relpath);
5574 /* Get some information from the parent */
5575 SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, &revision, &repos_relpath,
5576 &repos_id, NULL, NULL, NULL, NULL, NULL,
5579 src_parent_relpath, src_op_depth,
5580 scratch_pool, scratch_pool));
5582 if (repos_relpath == NULL)
5584 /* The node is a local addition and has no shadowed information */
5585 return SVN_NO_ERROR;
5588 /* And calculate the child repos relpath */
5589 repos_relpath = svn_relpath_join(repos_relpath,
5590 svn_relpath_basename(ocb->src_relpath,
5594 SVN_ERR(db_op_copy_shadowed_layer(
5595 ocb->src_wcroot, ocb->src_relpath, src_op_depth,
5596 ocb->dst_wcroot, ocb->dst_relpath, dst_op_depth,
5598 repos_id, repos_relpath, revision,
5599 (ocb->is_move ? dst_op_depth : 0),
5602 return SVN_NO_ERROR;
5606 svn_wc__db_op_copy_shadowed_layer(svn_wc__db_t *db,
5607 const char *src_abspath,
5608 const char *dst_abspath,
5609 svn_boolean_t is_move,
5610 apr_pool_t *scratch_pool)
5612 struct op_copy_baton ocb = {0};
5614 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
5615 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
5617 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
5618 &ocb.src_relpath, db,
5620 scratch_pool, scratch_pool));
5621 VERIFY_USABLE_WCROOT(ocb.src_wcroot);
5623 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
5626 scratch_pool, scratch_pool));
5627 VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
5629 ocb.is_move = is_move;
5630 ocb.dst_op_root_relpath = NULL; /* not used by op_copy_shadowed_layer_txn */
5632 ocb.work_items = NULL;
5634 /* Call with the sdb in src_wcroot. It might call itself again to
5635 also obtain a lock in dst_wcroot */
5636 SVN_WC__DB_WITH_TXN(op_copy_shadowed_layer_txn(ocb.src_wcroot, &ocb,
5640 return SVN_NO_ERROR;
5644 /* If there are any server-excluded base nodes then the copy must fail
5645 as it's not possible to commit such a copy.
5646 Return an error if there are any server-excluded nodes. */
5647 static svn_error_t *
5648 catch_copy_of_server_excluded(svn_wc__db_wcroot_t *wcroot,
5649 const char *local_relpath,
5650 apr_pool_t *scratch_pool)
5652 svn_sqlite__stmt_t *stmt;
5653 svn_boolean_t have_row;
5654 const char *server_excluded_relpath;
5656 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5657 STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
5658 SVN_ERR(svn_sqlite__bindf(stmt, "is",
5661 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5663 server_excluded_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
5664 SVN_ERR(svn_sqlite__reset(stmt));
5666 return svn_error_createf(SVN_ERR_AUTHZ_UNREADABLE, NULL,
5667 _("Cannot copy '%s' excluded by server"),
5668 path_for_error_message(wcroot,
5669 server_excluded_relpath,
5672 return SVN_NO_ERROR;
5677 svn_wc__db_op_copy_dir(svn_wc__db_t *db,
5678 const char *local_abspath,
5679 const apr_hash_t *props,
5680 svn_revnum_t changed_rev,
5681 apr_time_t changed_date,
5682 const char *changed_author,
5683 const char *original_repos_relpath,
5684 const char *original_root_url,
5685 const char *original_uuid,
5686 svn_revnum_t original_revision,
5687 const apr_array_header_t *children,
5689 svn_boolean_t is_move,
5690 const svn_skel_t *conflict,
5691 const svn_skel_t *work_items,
5692 apr_pool_t *scratch_pool)
5694 svn_wc__db_wcroot_t *wcroot;
5695 const char *local_relpath;
5696 insert_working_baton_t iwb;
5697 int parent_op_depth;
5699 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5700 SVN_ERR_ASSERT(props != NULL);
5701 /* ### any assertions for CHANGED_* ? */
5702 /* ### any assertions for ORIGINAL_* ? */
5704 SVN_ERR_ASSERT(children != NULL);
5707 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5708 local_abspath, scratch_pool, scratch_pool));
5709 VERIFY_USABLE_WCROOT(wcroot);
5713 iwb.presence = svn_wc__db_status_normal;
5714 iwb.kind = svn_node_dir;
5716 if (original_root_url != NULL)
5718 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5719 original_root_url, original_uuid,
5720 wcroot->sdb, scratch_pool));
5721 iwb.original_repos_relpath = original_repos_relpath;
5722 iwb.original_revnum = original_revision;
5725 iwb.changed_rev = changed_rev;
5726 iwb.changed_date = changed_date;
5727 iwb.changed_author = changed_author;
5730 /* ### Should we do this inside the transaction? */
5731 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5732 &parent_op_depth, iwb.original_repos_id,
5733 original_repos_relpath, original_revision,
5734 wcroot, local_relpath, scratch_pool));
5736 iwb.children = children;
5738 iwb.moved_here = is_move && (parent_op_depth == 0 ||
5739 iwb.op_depth == parent_op_depth);
5741 iwb.work_items = work_items;
5742 iwb.conflict = conflict;
5744 SVN_WC__DB_WITH_TXN(
5745 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5747 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
5749 return SVN_NO_ERROR;
5754 svn_wc__db_op_copy_file(svn_wc__db_t *db,
5755 const char *local_abspath,
5756 const apr_hash_t *props,
5757 svn_revnum_t changed_rev,
5758 apr_time_t changed_date,
5759 const char *changed_author,
5760 const char *original_repos_relpath,
5761 const char *original_root_url,
5762 const char *original_uuid,
5763 svn_revnum_t original_revision,
5764 const svn_checksum_t *checksum,
5765 svn_boolean_t update_actual_props,
5766 const apr_hash_t *new_actual_props,
5767 svn_boolean_t is_move,
5768 const svn_skel_t *conflict,
5769 const svn_skel_t *work_items,
5770 apr_pool_t *scratch_pool)
5772 svn_wc__db_wcroot_t *wcroot;
5773 const char *local_relpath;
5774 insert_working_baton_t iwb;
5775 int parent_op_depth;
5777 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5778 SVN_ERR_ASSERT(props != NULL);
5779 /* ### any assertions for CHANGED_* ? */
5780 SVN_ERR_ASSERT((! original_repos_relpath && ! original_root_url
5781 && ! original_uuid && ! checksum
5782 && original_revision == SVN_INVALID_REVNUM)
5783 || (original_repos_relpath && original_root_url
5784 && original_uuid && checksum
5785 && original_revision != SVN_INVALID_REVNUM));
5787 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5788 local_abspath, scratch_pool, scratch_pool));
5789 VERIFY_USABLE_WCROOT(wcroot);
5793 iwb.presence = svn_wc__db_status_normal;
5794 iwb.kind = svn_node_file;
5796 if (original_root_url != NULL)
5798 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5799 original_root_url, original_uuid,
5800 wcroot->sdb, scratch_pool));
5801 iwb.original_repos_relpath = original_repos_relpath;
5802 iwb.original_revnum = original_revision;
5805 iwb.changed_rev = changed_rev;
5806 iwb.changed_date = changed_date;
5807 iwb.changed_author = changed_author;
5810 /* ### Should we do this inside the transaction? */
5811 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5812 &parent_op_depth, iwb.original_repos_id,
5813 original_repos_relpath, original_revision,
5814 wcroot, local_relpath, scratch_pool));
5816 iwb.checksum = checksum;
5817 iwb.moved_here = is_move && (parent_op_depth == 0 ||
5818 iwb.op_depth == parent_op_depth);
5820 if (update_actual_props)
5822 iwb.update_actual_props = update_actual_props;
5823 iwb.new_actual_props = new_actual_props;
5826 iwb.work_items = work_items;
5827 iwb.conflict = conflict;
5829 SVN_WC__DB_WITH_TXN(
5830 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5832 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5834 return SVN_NO_ERROR;
5839 svn_wc__db_op_copy_symlink(svn_wc__db_t *db,
5840 const char *local_abspath,
5841 const apr_hash_t *props,
5842 svn_revnum_t changed_rev,
5843 apr_time_t changed_date,
5844 const char *changed_author,
5845 const char *original_repos_relpath,
5846 const char *original_root_url,
5847 const char *original_uuid,
5848 svn_revnum_t original_revision,
5850 svn_boolean_t is_move,
5851 const svn_skel_t *conflict,
5852 const svn_skel_t *work_items,
5853 apr_pool_t *scratch_pool)
5855 svn_wc__db_wcroot_t *wcroot;
5856 const char *local_relpath;
5857 insert_working_baton_t iwb;
5858 int parent_op_depth;
5860 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5861 SVN_ERR_ASSERT(props != NULL);
5862 /* ### any assertions for CHANGED_* ? */
5863 /* ### any assertions for ORIGINAL_* ? */
5864 SVN_ERR_ASSERT(target != NULL);
5866 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5867 local_abspath, scratch_pool, scratch_pool));
5868 VERIFY_USABLE_WCROOT(wcroot);
5872 iwb.presence = svn_wc__db_status_normal;
5873 iwb.kind = svn_node_symlink;
5876 if (original_root_url != NULL)
5878 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5879 original_root_url, original_uuid,
5880 wcroot->sdb, scratch_pool));
5881 iwb.original_repos_relpath = original_repos_relpath;
5882 iwb.original_revnum = original_revision;
5885 iwb.changed_rev = changed_rev;
5886 iwb.changed_date = changed_date;
5887 iwb.changed_author = changed_author;
5890 /* ### Should we do this inside the transaction? */
5891 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5892 &parent_op_depth, iwb.original_repos_id,
5893 original_repos_relpath, original_revision,
5894 wcroot, local_relpath, scratch_pool));
5896 iwb.target = target;
5897 iwb.moved_here = is_move && (parent_op_depth == 0 ||
5898 iwb.op_depth == parent_op_depth);
5900 iwb.work_items = work_items;
5901 iwb.conflict = conflict;
5903 SVN_WC__DB_WITH_TXN(
5904 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5906 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5908 return SVN_NO_ERROR;
5913 svn_wc__db_op_add_directory(svn_wc__db_t *db,
5914 const char *local_abspath,
5915 const apr_hash_t *props,
5916 const svn_skel_t *work_items,
5917 apr_pool_t *scratch_pool)
5919 svn_wc__db_wcroot_t *wcroot;
5920 const char *local_relpath;
5921 const char *dir_abspath;
5923 insert_working_baton_t iwb;
5925 /* Resolve wcroot via parent directory to avoid obstruction handling */
5926 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5927 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5929 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5930 dir_abspath, scratch_pool, scratch_pool));
5931 VERIFY_USABLE_WCROOT(wcroot);
5935 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5936 iwb.presence = svn_wc__db_status_normal;
5937 iwb.kind = svn_node_dir;
5938 iwb.op_depth = relpath_depth(local_relpath);
5939 if (props && apr_hash_count((apr_hash_t *)props))
5941 iwb.update_actual_props = TRUE;
5942 iwb.new_actual_props = props;
5945 iwb.work_items = work_items;
5947 SVN_WC__DB_WITH_TXN(
5948 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5950 /* Use depth infinity to make sure we have no invalid cached information
5951 * about children of this dir. */
5952 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5955 return SVN_NO_ERROR;
5960 svn_wc__db_op_add_file(svn_wc__db_t *db,
5961 const char *local_abspath,
5962 const apr_hash_t *props,
5963 const svn_skel_t *work_items,
5964 apr_pool_t *scratch_pool)
5966 svn_wc__db_wcroot_t *wcroot;
5967 const char *local_relpath;
5968 insert_working_baton_t iwb;
5969 const char *dir_abspath;
5972 /* Resolve wcroot via parent directory to avoid obstruction handling */
5973 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5974 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5976 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5977 dir_abspath, scratch_pool, scratch_pool));
5978 VERIFY_USABLE_WCROOT(wcroot);
5982 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5983 iwb.presence = svn_wc__db_status_normal;
5984 iwb.kind = svn_node_file;
5985 iwb.op_depth = relpath_depth(local_relpath);
5986 if (props && apr_hash_count((apr_hash_t *)props))
5988 iwb.update_actual_props = TRUE;
5989 iwb.new_actual_props = props;
5992 iwb.work_items = work_items;
5994 SVN_WC__DB_WITH_TXN(
5995 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5997 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5999 return SVN_NO_ERROR;
6004 svn_wc__db_op_add_symlink(svn_wc__db_t *db,
6005 const char *local_abspath,
6007 const apr_hash_t *props,
6008 const svn_skel_t *work_items,
6009 apr_pool_t *scratch_pool)
6011 svn_wc__db_wcroot_t *wcroot;
6012 const char *local_relpath;
6013 insert_working_baton_t iwb;
6014 const char *dir_abspath;
6017 /* Resolve wcroot via parent directory to avoid obstruction handling */
6018 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6019 SVN_ERR_ASSERT(target != NULL);
6021 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
6023 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6024 dir_abspath, scratch_pool, scratch_pool));
6026 VERIFY_USABLE_WCROOT(wcroot);
6030 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
6031 iwb.presence = svn_wc__db_status_normal;
6032 iwb.kind = svn_node_symlink;
6033 iwb.op_depth = relpath_depth(local_relpath);
6034 if (props && apr_hash_count((apr_hash_t *)props))
6036 iwb.update_actual_props = TRUE;
6037 iwb.new_actual_props = props;
6040 iwb.target = target;
6042 iwb.work_items = work_items;
6044 SVN_WC__DB_WITH_TXN(
6045 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
6047 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6049 return SVN_NO_ERROR;
6052 /* Record RECORDED_SIZE and RECORDED_TIME into top layer in NODES */
6053 static svn_error_t *
6054 db_record_fileinfo(svn_wc__db_wcroot_t *wcroot,
6055 const char *local_relpath,
6056 apr_int64_t recorded_size,
6057 apr_int64_t recorded_time,
6058 apr_pool_t *scratch_pool)
6060 svn_sqlite__stmt_t *stmt;
6063 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6064 STMT_UPDATE_NODE_FILEINFO));
6065 SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
6066 recorded_size, recorded_time));
6067 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6069 SVN_ERR_ASSERT(affected_rows == 1);
6071 return SVN_NO_ERROR;
6076 svn_wc__db_global_record_fileinfo(svn_wc__db_t *db,
6077 const char *local_abspath,
6078 svn_filesize_t recorded_size,
6079 apr_time_t recorded_time,
6080 apr_pool_t *scratch_pool)
6082 svn_wc__db_wcroot_t *wcroot;
6083 const char *local_relpath;
6085 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6087 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6088 local_abspath, scratch_pool, scratch_pool));
6089 VERIFY_USABLE_WCROOT(wcroot);
6091 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
6092 recorded_size, recorded_time, scratch_pool));
6094 /* We *totally* monkeyed the entries. Toss 'em. */
6095 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6097 return SVN_NO_ERROR;
6101 /* Set the ACTUAL_NODE properties column for (WC_ID, LOCAL_RELPATH) to
6104 * Note: PROPS=NULL means the actual props are the same as the pristine
6105 * props; to indicate no properties when the pristine has some props,
6106 * PROPS must be an empty hash. */
6107 static svn_error_t *
6108 set_actual_props(svn_wc__db_wcroot_t *wcroot,
6109 const char *local_relpath,
6111 apr_pool_t *scratch_pool)
6113 svn_sqlite__stmt_t *stmt;
6116 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6117 STMT_UPDATE_ACTUAL_PROPS));
6118 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6119 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
6120 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6122 if (affected_rows == 1 || !props)
6124 /* Perhaps the entire ACTUAL record is unneeded now? */
6125 if (!props && affected_rows)
6127 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6128 STMT_DELETE_ACTUAL_EMPTY));
6129 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6130 SVN_ERR(svn_sqlite__step_done(stmt));
6133 return SVN_NO_ERROR; /* We are done */
6136 /* We have to insert a row in ACTUAL */
6138 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6139 STMT_INSERT_ACTUAL_PROPS));
6140 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6141 if (*local_relpath != '\0')
6142 SVN_ERR(svn_sqlite__bind_text(stmt, 3,
6143 svn_relpath_dirname(local_relpath,
6145 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
6146 return svn_error_trace(svn_sqlite__step_done(stmt));
6150 svn_wc__db_op_set_props_internal(svn_wc__db_wcroot_t *wcroot,
6151 const char *local_relpath,
6153 svn_boolean_t clear_recorded_info,
6154 apr_pool_t *scratch_pool)
6156 SVN_ERR(set_actual_props(wcroot, local_relpath, props, scratch_pool));
6158 if (clear_recorded_info)
6160 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
6161 SVN_INVALID_FILESIZE, 0,
6165 return SVN_NO_ERROR;
6168 /* The body of svn_wc__db_op_set_props().
6170 Set the 'properties' column in the 'ACTUAL_NODE' table to BATON->props.
6171 Create an entry in the ACTUAL table for the node if it does not yet
6173 To specify no properties, BATON->props must be an empty hash, not NULL.
6174 BATON is of type 'struct set_props_baton_t'.
6176 static svn_error_t *
6177 set_props_txn(svn_wc__db_wcroot_t *wcroot,
6178 const char *local_relpath,
6180 svn_boolean_t clear_recorded_info,
6181 const svn_skel_t *conflict,
6182 const svn_skel_t *work_items,
6183 apr_pool_t *scratch_pool)
6185 apr_hash_t *pristine_props;
6187 /* Check if the props are modified. If no changes, then wipe out the
6188 ACTUAL props. PRISTINE_PROPS==NULL means that any
6189 ACTUAL props are okay as provided, so go ahead and set them. */
6190 SVN_ERR(db_read_pristine_props(&pristine_props, wcroot, local_relpath, FALSE,
6191 scratch_pool, scratch_pool));
6192 if (props && pristine_props)
6194 apr_array_header_t *prop_diffs;
6196 SVN_ERR(svn_prop_diffs(&prop_diffs, props, pristine_props,
6198 if (prop_diffs->nelts == 0)
6202 SVN_ERR(svn_wc__db_op_set_props_internal(wcroot, local_relpath, props,
6203 clear_recorded_info, scratch_pool));
6206 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6208 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6209 conflict, scratch_pool));
6211 return SVN_NO_ERROR;
6216 svn_wc__db_op_set_props(svn_wc__db_t *db,
6217 const char *local_abspath,
6219 svn_boolean_t clear_recorded_info,
6220 const svn_skel_t *conflict,
6221 const svn_skel_t *work_items,
6222 apr_pool_t *scratch_pool)
6224 svn_wc__db_wcroot_t *wcroot;
6225 const char *local_relpath;
6227 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6229 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6230 db, local_abspath, scratch_pool, scratch_pool));
6231 VERIFY_USABLE_WCROOT(wcroot);
6233 SVN_WC__DB_WITH_TXN(set_props_txn(wcroot, local_relpath, props,
6234 clear_recorded_info, conflict, work_items,
6237 return SVN_NO_ERROR;
6242 svn_wc__db_op_modified(svn_wc__db_t *db,
6243 const char *local_abspath,
6244 apr_pool_t *scratch_pool)
6246 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6252 static svn_error_t *
6253 populate_targets_tree(svn_wc__db_wcroot_t *wcroot,
6254 const char *local_relpath,
6256 const apr_array_header_t *changelist_filter,
6257 apr_pool_t *scratch_pool)
6259 svn_sqlite__stmt_t *stmt;
6260 int affected_rows = 0;
6261 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6262 STMT_CREATE_TARGETS_LIST));
6264 if (changelist_filter && changelist_filter->nelts > 0)
6266 /* Iterate over the changelists, adding the nodes which match.
6267 Common case: we only have one changelist, so this only
6274 case svn_depth_empty:
6275 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST;
6278 case svn_depth_files:
6279 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES;
6282 case svn_depth_immediates:
6283 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES;
6286 case svn_depth_infinity:
6287 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY;
6291 /* We don't know how to handle unknown or exclude. */
6292 SVN_ERR_MALFUNCTION();
6296 for (i = 0; i < changelist_filter->nelts; i++)
6299 const char *changelist = APR_ARRAY_IDX(changelist_filter, i,
6302 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6303 STMT_INSERT_TARGET_WITH_CHANGELIST));
6304 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
6305 local_relpath, changelist));
6306 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6308 /* If the root is matched by the changelist, we don't have to match
6309 the children. As that tells us the root is a file */
6310 if (!sub_affected && depth > svn_depth_empty)
6312 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
6313 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
6314 local_relpath, changelist));
6315 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6318 affected_rows += sub_affected;
6321 else /* No changelist filtering */
6328 case svn_depth_empty:
6329 stmt_idx = STMT_INSERT_TARGET;
6332 case svn_depth_files:
6333 stmt_idx = STMT_INSERT_TARGET_DEPTH_FILES;
6336 case svn_depth_immediates:
6337 stmt_idx = STMT_INSERT_TARGET_DEPTH_IMMEDIATES;
6340 case svn_depth_infinity:
6341 stmt_idx = STMT_INSERT_TARGET_DEPTH_INFINITY;
6345 /* We don't know how to handle unknown or exclude. */
6346 SVN_ERR_MALFUNCTION();
6350 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6351 STMT_INSERT_TARGET));
6352 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6353 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6354 affected_rows += sub_affected;
6356 if (depth > svn_depth_empty)
6358 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
6359 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6360 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6361 affected_rows += sub_affected;
6365 /* Does the target exist? */
6366 if (affected_rows == 0)
6368 svn_boolean_t exists;
6369 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
6372 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6373 _("The node '%s' was not found."),
6374 path_for_error_message(wcroot,
6379 return SVN_NO_ERROR;
6384 static svn_error_t *
6385 dump_targets(svn_wc__db_wcroot_t *wcroot,
6386 apr_pool_t *scratch_pool)
6388 svn_sqlite__stmt_t *stmt;
6389 svn_boolean_t have_row;
6391 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6392 STMT_SELECT_TARGETS));
6393 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6396 const char *target = svn_sqlite__column_text(stmt, 0, NULL);
6397 SVN_DBG(("Target: '%s'\n", target));
6398 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6401 SVN_ERR(svn_sqlite__reset(stmt));
6403 return SVN_NO_ERROR;
6408 struct set_changelist_baton_t
6410 const char *new_changelist;
6411 const apr_array_header_t *changelist_filter;
6416 /* The main part of svn_wc__db_op_set_changelist().
6418 * Implements svn_wc__db_txn_callback_t. */
6419 static svn_error_t *
6420 set_changelist_txn(void *baton,
6421 svn_wc__db_wcroot_t *wcroot,
6422 const char *local_relpath,
6423 apr_pool_t *scratch_pool)
6425 struct set_changelist_baton_t *scb = baton;
6426 svn_sqlite__stmt_t *stmt;
6428 SVN_ERR(populate_targets_tree(wcroot, local_relpath, scb->depth,
6429 scb->changelist_filter, scratch_pool));
6431 /* Ensure we have actual nodes for our targets. */
6432 if (scb->new_changelist)
6434 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6435 STMT_INSERT_ACTUAL_EMPTIES_FILES));
6436 SVN_ERR(svn_sqlite__step_done(stmt));
6439 /* Now create our notification table. */
6440 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6441 STMT_CREATE_CHANGELIST_LIST));
6442 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6443 STMT_CREATE_CHANGELIST_TRIGGER));
6445 /* Update our changelists. */
6446 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6447 STMT_UPDATE_ACTUAL_CHANGELISTS));
6448 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6449 scb->new_changelist));
6450 SVN_ERR(svn_sqlite__step_done(stmt));
6452 if (scb->new_changelist)
6454 /* We have to notify that we skipped directories, so do that now. */
6455 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6456 STMT_MARK_SKIPPED_CHANGELIST_DIRS));
6457 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6458 scb->new_changelist));
6459 SVN_ERR(svn_sqlite__step_done(stmt));
6462 /* We may have left empty ACTUAL nodes, so remove them. This is only a
6463 potential problem if we removed changelists. */
6464 if (!scb->new_changelist)
6466 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6467 STMT_DELETE_ACTUAL_EMPTIES));
6468 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6469 SVN_ERR(svn_sqlite__step_done(stmt));
6472 return SVN_NO_ERROR;
6476 /* Send notifications for svn_wc__db_op_set_changelist().
6478 * Implements work_callback_t. */
6479 static svn_error_t *
6480 do_changelist_notify(void *baton,
6481 svn_wc__db_wcroot_t *wcroot,
6482 svn_cancel_func_t cancel_func,
6484 svn_wc_notify_func2_t notify_func,
6486 apr_pool_t *scratch_pool)
6488 svn_sqlite__stmt_t *stmt;
6489 svn_boolean_t have_row;
6490 apr_pool_t *iterpool;
6492 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6493 STMT_SELECT_CHANGELIST_LIST));
6494 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6496 iterpool = svn_pool_create(scratch_pool);
6499 /* ### wc_id is column 0. use it one day... */
6500 const char *notify_relpath = svn_sqlite__column_text(stmt, 1, NULL);
6501 svn_wc_notify_action_t action = svn_sqlite__column_int(stmt, 2);
6502 svn_wc_notify_t *notify;
6503 const char *notify_abspath;
6505 svn_pool_clear(iterpool);
6509 svn_error_t *err = cancel_func(cancel_baton);
6512 return svn_error_trace(svn_error_compose_create(
6514 svn_sqlite__reset(stmt)));
6517 notify_abspath = svn_dirent_join(wcroot->abspath, notify_relpath,
6519 notify = svn_wc_create_notify(notify_abspath, action, iterpool);
6520 notify->changelist_name = svn_sqlite__column_text(stmt, 3, NULL);
6521 notify_func(notify_baton, notify, iterpool);
6523 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6525 svn_pool_destroy(iterpool);
6527 return svn_error_trace(svn_sqlite__reset(stmt));
6532 svn_wc__db_op_set_changelist(svn_wc__db_t *db,
6533 const char *local_abspath,
6534 const char *new_changelist,
6535 const apr_array_header_t *changelist_filter,
6537 svn_wc_notify_func2_t notify_func,
6539 svn_cancel_func_t cancel_func,
6541 apr_pool_t *scratch_pool)
6543 svn_wc__db_wcroot_t *wcroot;
6544 const char *local_relpath;
6545 struct set_changelist_baton_t scb;
6547 scb.new_changelist = new_changelist;
6548 scb.changelist_filter = changelist_filter;
6551 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6553 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6555 scratch_pool, scratch_pool));
6556 VERIFY_USABLE_WCROOT(wcroot);
6558 /* Flush the entries before we do the work. Even if no work is performed,
6559 the flush isn't a problem. */
6560 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6562 /* Perform the set-changelist operation (transactionally), perform any
6563 notifications necessary, and then clean out our temporary tables. */
6564 return svn_error_trace(with_finalization(wcroot, local_relpath,
6565 set_changelist_txn, &scb,
6566 do_changelist_notify, NULL,
6567 cancel_func, cancel_baton,
6568 notify_func, notify_baton,
6569 STMT_FINALIZE_CHANGELIST,
6573 /* Implementation of svn_wc__db_op_mark_conflict() */
6575 svn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t *wcroot,
6576 const char *local_relpath,
6577 const svn_skel_t *conflict_skel,
6578 apr_pool_t *scratch_pool)
6580 svn_sqlite__stmt_t *stmt;
6581 svn_boolean_t got_row;
6582 svn_boolean_t is_complete;
6584 SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict_skel));
6585 SVN_ERR_ASSERT(is_complete);
6587 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6588 STMT_SELECT_ACTUAL_NODE));
6589 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6590 SVN_ERR(svn_sqlite__step(&got_row, stmt));
6591 SVN_ERR(svn_sqlite__reset(stmt));
6595 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6596 STMT_UPDATE_ACTUAL_CONFLICT));
6597 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6601 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6602 STMT_INSERT_ACTUAL_CONFLICT));
6603 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6604 if (*local_relpath != '\0')
6605 SVN_ERR(svn_sqlite__bind_text(stmt, 4,
6606 svn_relpath_dirname(local_relpath,
6611 svn_stringbuf_t *sb = svn_skel__unparse(conflict_skel, scratch_pool);
6613 SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6616 SVN_ERR(svn_sqlite__update(NULL, stmt));
6618 return SVN_NO_ERROR;
6622 svn_wc__db_op_mark_conflict(svn_wc__db_t *db,
6623 const char *local_abspath,
6624 const svn_skel_t *conflict_skel,
6625 const svn_skel_t *work_items,
6626 apr_pool_t *scratch_pool)
6628 svn_wc__db_wcroot_t *wcroot;
6629 const char *local_relpath;
6631 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6633 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6634 local_abspath, scratch_pool, scratch_pool));
6635 VERIFY_USABLE_WCROOT(wcroot);
6637 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6638 conflict_skel, scratch_pool));
6640 /* ### Should be handled in the same transaction as setting the conflict */
6642 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6644 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6646 return SVN_NO_ERROR;
6650 /* The body of svn_wc__db_op_mark_resolved().
6653 svn_wc__db_op_mark_resolved_internal(svn_wc__db_wcroot_t *wcroot,
6654 const char *local_relpath,
6656 svn_boolean_t resolved_text,
6657 svn_boolean_t resolved_props,
6658 svn_boolean_t resolved_tree,
6659 const svn_skel_t *work_items,
6660 apr_pool_t *scratch_pool)
6662 svn_sqlite__stmt_t *stmt;
6663 svn_boolean_t have_row;
6664 int total_affected_rows = 0;
6665 svn_boolean_t resolved_all;
6666 apr_size_t conflict_len;
6667 const void *conflict_data;
6668 svn_skel_t *conflicts;
6670 /* Check if we have a conflict in ACTUAL */
6671 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6672 STMT_SELECT_ACTUAL_NODE));
6673 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6675 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6679 SVN_ERR(svn_sqlite__reset(stmt));
6681 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6682 STMT_SELECT_NODE_INFO));
6684 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6686 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6687 SVN_ERR(svn_sqlite__reset(stmt));
6690 return SVN_NO_ERROR;
6692 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6693 _("The node '%s' was not found."),
6694 path_for_error_message(wcroot,
6699 conflict_data = svn_sqlite__column_blob(stmt, 2, &conflict_len,
6701 conflicts = svn_skel__parse(conflict_data, conflict_len, scratch_pool);
6702 SVN_ERR(svn_sqlite__reset(stmt));
6704 SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
6705 db, wcroot->abspath,
6707 resolved_props ? "" : NULL,
6709 scratch_pool, scratch_pool));
6711 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6712 STMT_UPDATE_ACTUAL_CONFLICT));
6713 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6717 svn_stringbuf_t *sb = svn_skel__unparse(conflicts, scratch_pool);
6719 SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6722 SVN_ERR(svn_sqlite__update(&total_affected_rows, stmt));
6724 /* Now, remove the actual node if it doesn't have any more useful
6725 information. We only need to do this if we've remove data ourselves. */
6726 if (total_affected_rows > 0)
6728 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6729 STMT_DELETE_ACTUAL_EMPTY));
6730 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6731 SVN_ERR(svn_sqlite__step_done(stmt));
6734 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6736 return SVN_NO_ERROR;
6740 svn_wc__db_op_mark_resolved(svn_wc__db_t *db,
6741 const char *local_abspath,
6742 svn_boolean_t resolved_text,
6743 svn_boolean_t resolved_props,
6744 svn_boolean_t resolved_tree,
6745 const svn_skel_t *work_items,
6746 apr_pool_t *scratch_pool)
6748 svn_wc__db_wcroot_t *wcroot;
6749 const char *local_relpath;
6751 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6753 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6754 local_abspath, scratch_pool, scratch_pool));
6755 VERIFY_USABLE_WCROOT(wcroot);
6757 SVN_WC__DB_WITH_TXN(
6758 svn_wc__db_op_mark_resolved_internal(
6759 wcroot, local_relpath, db,
6760 resolved_text, resolved_props, resolved_tree,
6761 work_items, scratch_pool),
6764 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6765 return SVN_NO_ERROR;
6768 /* Clear moved-to information at the delete-half of the move which moved
6769 * MOVED_TO_RELPATH here. This transforms the delete part of the move into a
6772 * Note that the moved-to location is always an op-root, while this is not the
6773 * case for a moved-from location.
6775 static svn_error_t *
6776 clear_moved_to(svn_wc__db_wcroot_t *wcroot,
6777 const char *moved_to_relpath,
6778 apr_pool_t *scratch_pool)
6780 svn_sqlite__stmt_t *stmt;
6781 const char *moved_from_relpath;
6782 int moved_from_op_depth;
6784 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6785 STMT_SELECT_MOVED_FROM_RELPATH));
6786 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, moved_to_relpath));
6787 SVN_ERR(svn_sqlite__step_row(stmt));
6789 moved_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
6790 moved_from_op_depth = svn_sqlite__column_int(stmt, 1);
6791 SVN_ERR(svn_sqlite__reset(stmt));
6793 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6794 STMT_CLEAR_MOVED_TO_RELPATH));
6795 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6796 moved_from_relpath, moved_from_op_depth));
6797 SVN_ERR(svn_sqlite__update(NULL, stmt));
6799 return SVN_NO_ERROR;
6802 /* Helper function for op_revert_txn. Raises move tree conflicts on
6803 descendants to ensure database stability on a non recursive revert
6804 of an ancestor that contains a possible move related tree conflict.
6806 static svn_error_t *
6807 revert_maybe_raise_moved_away(svn_wc__db_wcroot_t * wcroot,
6809 const char *local_relpath,
6811 apr_pool_t *scratch_pool)
6813 svn_skel_t *conflict;
6814 svn_wc_operation_t operation;
6815 svn_boolean_t tree_conflicted;
6816 const apr_array_header_t *locations;
6817 svn_wc_conflict_reason_t reason;
6818 svn_wc_conflict_action_t action;
6820 SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, wcroot,
6822 scratch_pool, scratch_pool));
6825 return SVN_NO_ERROR;
6827 SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, NULL, NULL,
6829 db, wcroot->abspath,
6831 scratch_pool, scratch_pool));
6833 if (!tree_conflicted
6834 || (operation != svn_wc_operation_update
6835 && operation != svn_wc_operation_switch))
6837 return SVN_NO_ERROR;
6840 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
6842 db, wcroot->abspath,
6847 if (reason == svn_wc_conflict_reason_deleted
6848 || reason == svn_wc_conflict_reason_replaced)
6850 SVN_ERR(svn_wc__db_op_raise_moved_away_internal(
6851 wcroot, local_relpath, op_depth_below, db,
6853 (locations && locations->nelts > 0)
6854 ? APR_ARRAY_IDX(locations, 0,
6855 const svn_wc_conflict_version_t *)
6857 (locations && locations->nelts > 1)
6858 ? APR_ARRAY_IDX(locations, 1,
6859 const svn_wc_conflict_version_t *)
6863 /* Transform the move information into revert information */
6864 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6865 STMT_MOVE_NOTIFY_TO_REVERT));
6868 return SVN_NO_ERROR;
6871 /* Baton for op_revert_txn and op_revert_recursive_txn */
6872 struct revert_baton_t
6875 svn_boolean_t clear_changelists;
6878 /* One of the two alternative bodies of svn_wc__db_op_revert().
6880 * Implements svn_wc__db_txn_callback_t. */
6881 static svn_error_t *
6882 op_revert_txn(void *baton,
6883 svn_wc__db_wcroot_t *wcroot,
6884 const char *local_relpath,
6885 apr_pool_t *scratch_pool)
6887 struct revert_baton_t *rvb = baton;
6888 svn_wc__db_t *db = rvb->db;
6889 svn_sqlite__stmt_t *stmt;
6890 svn_boolean_t have_row;
6892 svn_boolean_t moved_here;
6894 const char *moved_to;
6897 /* ### Similar structure to op_revert_recursive_txn, should they be
6900 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6901 STMT_SELECT_NODE_INFO));
6902 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6903 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6906 SVN_ERR(svn_sqlite__reset(stmt));
6908 /* There was no NODE row, so attempt to delete an ACTUAL_NODE row. */
6909 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6910 STMT_DELETE_ACTUAL_NODE));
6911 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6912 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6915 /* Can't do non-recursive actual-only revert if actual-only
6916 children exist. Raise an error to cancel the transaction. */
6917 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6918 STMT_ACTUAL_HAS_CHILDREN));
6919 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6920 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6921 SVN_ERR(svn_sqlite__reset(stmt));
6923 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6924 _("Can't revert '%s' without"
6925 " reverting children"),
6926 path_for_error_message(wcroot,
6929 return SVN_NO_ERROR;
6932 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6933 _("The node '%s' was not found."),
6934 path_for_error_message(wcroot,
6939 op_depth = svn_sqlite__column_int(stmt, 0);
6940 moved_here = svn_sqlite__column_boolean(stmt, 15);
6941 moved_to = svn_sqlite__column_text(stmt, 17, scratch_pool);
6943 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6945 op_depth_below = svn_sqlite__column_int(stmt, 0);
6947 op_depth_below = -1;
6949 SVN_ERR(svn_sqlite__reset(stmt));
6953 SVN_ERR(svn_wc__db_op_break_move_internal(wcroot,
6954 local_relpath, op_depth,
6955 moved_to, NULL, scratch_pool));
6958 if (op_depth > 0 && op_depth == relpath_depth(local_relpath))
6960 int op_depth_increased;
6962 /* Can't do non-recursive revert if children exist */
6963 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6964 STMT_SELECT_GE_OP_DEPTH_CHILDREN));
6965 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6966 local_relpath, op_depth));
6967 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6968 SVN_ERR(svn_sqlite__reset(stmt));
6970 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6971 _("Can't revert '%s' without"
6972 " reverting children"),
6973 path_for_error_message(wcroot,
6977 /* Rewrite the op-depth of all deleted children making the
6978 direct children into roots of deletes. */
6979 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6980 STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE));
6981 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6984 SVN_ERR(svn_sqlite__update(&op_depth_increased, stmt));
6986 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6987 STMT_DELETE_WORKING_NODE));
6988 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6989 SVN_ERR(svn_sqlite__step_done(stmt));
6991 /* ### This removes the lock, but what about the access baton? */
6992 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6993 STMT_DELETE_WC_LOCK_ORPHAN));
6994 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6995 SVN_ERR(svn_sqlite__step_done(stmt));
6997 /* If this node was moved-here, clear moved-to at the move source. */
6999 SVN_ERR(clear_moved_to(wcroot, local_relpath, scratch_pool));
7001 /* If the node was moved itself, we don't have interesting moved
7002 children (and the move itself was already broken) */
7003 if (op_depth_increased && !moved_to)
7004 SVN_ERR(revert_maybe_raise_moved_away(wcroot, db, local_relpath,
7005 op_depth_below, scratch_pool));
7008 if (rvb->clear_changelists)
7010 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7011 STMT_DELETE_ACTUAL_NODE));
7012 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7013 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7017 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7018 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST));
7019 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7020 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7023 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7024 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST));
7025 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7026 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7030 return SVN_NO_ERROR;
7034 /* One of the two alternative bodies of svn_wc__db_op_revert().
7036 * Implements svn_wc__db_txn_callback_t. */
7037 static svn_error_t *
7038 op_revert_recursive_txn(void *baton,
7039 svn_wc__db_wcroot_t *wcroot,
7040 const char *local_relpath,
7041 apr_pool_t *scratch_pool)
7043 struct revert_baton_t *rvb = baton;
7044 svn_sqlite__stmt_t *stmt;
7045 svn_boolean_t have_row;
7047 int select_op_depth;
7048 svn_boolean_t moved_here;
7050 apr_pool_t *iterpool;
7052 /* ### Similar structure to op_revert_txn, should they be
7055 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7056 STMT_SELECT_NODE_INFO));
7057 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7058 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7061 SVN_ERR(svn_sqlite__reset(stmt));
7063 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7064 STMT_DELETE_ACTUAL_NODE));
7065 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7067 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7070 return SVN_NO_ERROR; /* actual-only revert */
7072 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7073 _("The node '%s' was not found."),
7074 path_for_error_message(wcroot,
7079 op_depth = svn_sqlite__column_int(stmt, 0);
7080 moved_here = svn_sqlite__column_boolean(stmt, 15);
7081 SVN_ERR(svn_sqlite__reset(stmt));
7083 if (op_depth > 0 && op_depth != relpath_depth(local_relpath))
7084 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
7085 _("Can't revert '%s' without"
7086 " reverting parent"),
7087 path_for_error_message(wcroot,
7091 /* Remove moved-here from move destinations outside the tree. */
7092 SVN_ERR(svn_sqlite__get_statement(
7093 &stmt, wcroot->sdb, STMT_SELECT_MOVED_OUTSIDE));
7094 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
7096 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7099 const char *src_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7100 const char *dst_relpath = svn_sqlite__column_text(stmt, 1, NULL);
7101 int move_op_depth = svn_sqlite__column_int(stmt, 2);
7104 err = svn_wc__db_op_break_move_internal(wcroot,
7105 src_relpath, move_op_depth,
7106 dst_relpath, NULL, scratch_pool);
7108 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
7110 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7112 SVN_ERR(svn_sqlite__reset(stmt));
7114 /* Don't delete BASE nodes */
7115 select_op_depth = op_depth ? op_depth : 1;
7117 /* Reverting any non wc-root node */
7118 SVN_ERR(svn_sqlite__get_statement(
7120 STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
7121 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
7122 local_relpath, select_op_depth));
7123 SVN_ERR(svn_sqlite__step_done(stmt));
7125 if (rvb->clear_changelists)
7127 SVN_ERR(svn_sqlite__get_statement(
7129 STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7130 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7131 SVN_ERR(svn_sqlite__step_done(stmt));
7135 SVN_ERR(svn_sqlite__get_statement(
7137 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
7138 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7139 SVN_ERR(svn_sqlite__step_done(stmt));
7141 SVN_ERR(svn_sqlite__get_statement(
7143 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
7144 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7145 SVN_ERR(svn_sqlite__step_done(stmt));
7148 /* ### This removes the locks, but what about the access batons? */
7149 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7150 STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
7151 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7153 SVN_ERR(svn_sqlite__step_done(stmt));
7155 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7156 STMT_SELECT_MOVED_HERE_CHILDREN));
7157 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7159 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7161 iterpool = svn_pool_create(scratch_pool);
7164 const char *moved_here_child_relpath;
7167 svn_pool_clear(iterpool);
7169 moved_here_child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
7170 err = clear_moved_to(wcroot, moved_here_child_relpath, iterpool);
7172 return svn_error_trace(svn_error_compose_create(
7174 svn_sqlite__reset(stmt)));
7176 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7178 SVN_ERR(svn_sqlite__reset(stmt));
7179 svn_pool_destroy(iterpool);
7181 /* Clear potential moved-to pointing at the target node itself. */
7182 if (op_depth > 0 && op_depth == relpath_depth(local_relpath)
7184 SVN_ERR(clear_moved_to(wcroot, local_relpath, scratch_pool));
7186 return SVN_NO_ERROR;
7190 svn_wc__db_op_revert(svn_wc__db_t *db,
7191 const char *local_abspath,
7193 svn_boolean_t clear_changelists,
7194 apr_pool_t *result_pool,
7195 apr_pool_t *scratch_pool)
7197 svn_wc__db_wcroot_t *wcroot;
7198 const char *local_relpath;
7199 struct revert_baton_t rvb;
7200 struct with_triggers_baton_t wtb = { STMT_CREATE_REVERT_LIST,
7201 STMT_DROP_REVERT_LIST_TRIGGERS,
7204 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7207 rvb.clear_changelists = clear_changelists;
7208 wtb.cb_baton = &rvb;
7212 case svn_depth_empty:
7213 wtb.cb_func = op_revert_txn;
7215 case svn_depth_infinity:
7216 wtb.cb_func = op_revert_recursive_txn;
7219 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
7220 _("Unsupported depth for revert of '%s'"),
7221 svn_dirent_local_style(local_abspath,
7225 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7226 db, local_abspath, scratch_pool, scratch_pool));
7227 VERIFY_USABLE_WCROOT(wcroot);
7229 SVN_WC__DB_WITH_TXN(with_triggers(&wtb, wcroot, local_relpath, scratch_pool),
7232 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
7234 return SVN_NO_ERROR;
7237 /* The body of svn_wc__db_revert_list_read().
7239 static svn_error_t *
7240 revert_list_read(svn_boolean_t *reverted,
7241 const apr_array_header_t **marker_paths,
7242 svn_boolean_t *copied_here,
7243 svn_node_kind_t *kind,
7244 svn_wc__db_wcroot_t *wcroot,
7245 const char *local_relpath,
7247 apr_pool_t *result_pool,
7248 apr_pool_t *scratch_pool)
7250 svn_sqlite__stmt_t *stmt;
7251 svn_boolean_t have_row;
7254 *marker_paths = NULL;
7255 *copied_here = FALSE;
7256 *kind = svn_node_unknown;
7258 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7259 STMT_SELECT_REVERT_LIST));
7260 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7261 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7264 svn_boolean_t is_actual = svn_sqlite__column_boolean(stmt, 0);
7265 svn_boolean_t another_row = FALSE;
7269 apr_size_t conflict_len;
7270 const void *conflict_data;
7272 conflict_data = svn_sqlite__column_blob(stmt, 5, &conflict_len,
7276 svn_skel_t *conflicts = svn_skel__parse(conflict_data,
7280 SVN_ERR(svn_wc__conflict_read_markers(marker_paths,
7281 db, wcroot->abspath,
7287 if (!svn_sqlite__column_is_null(stmt, 1)) /* notify */
7290 SVN_ERR(svn_sqlite__step(&another_row, stmt));
7293 if (!is_actual || another_row)
7296 if (!svn_sqlite__column_is_null(stmt, 4)) /* repos_id */
7298 int op_depth = svn_sqlite__column_int(stmt, 3);
7299 *copied_here = (op_depth == relpath_depth(local_relpath));
7301 *kind = svn_sqlite__column_token(stmt, 2, kind_map);
7305 SVN_ERR(svn_sqlite__reset(stmt));
7309 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7310 STMT_DELETE_REVERT_LIST));
7311 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7312 SVN_ERR(svn_sqlite__step_done(stmt));
7315 return SVN_NO_ERROR;
7319 svn_wc__db_revert_list_read(svn_boolean_t *reverted,
7320 const apr_array_header_t **marker_files,
7321 svn_boolean_t *copied_here,
7322 svn_node_kind_t *kind,
7324 const char *local_abspath,
7325 apr_pool_t *result_pool,
7326 apr_pool_t *scratch_pool)
7328 svn_wc__db_wcroot_t *wcroot;
7329 const char *local_relpath;
7331 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7332 db, local_abspath, scratch_pool, scratch_pool));
7333 VERIFY_USABLE_WCROOT(wcroot);
7335 SVN_WC__DB_WITH_TXN(
7336 revert_list_read(reverted, marker_files, copied_here, kind,
7337 wcroot, local_relpath, db,
7338 result_pool, scratch_pool),
7340 return SVN_NO_ERROR;
7344 /* The body of svn_wc__db_revert_list_read_copied_children().
7346 static svn_error_t *
7347 revert_list_read_copied_children(svn_wc__db_wcroot_t *wcroot,
7348 const char *local_relpath,
7349 apr_array_header_t **children_p,
7350 apr_pool_t *result_pool,
7351 apr_pool_t *scratch_pool)
7353 svn_sqlite__stmt_t *stmt;
7354 svn_boolean_t have_row;
7355 apr_array_header_t *children;
7358 apr_array_make(result_pool, 0,
7359 sizeof(svn_wc__db_revert_list_copied_child_info_t *));
7361 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7362 STMT_SELECT_REVERT_LIST_COPIED_CHILDREN));
7363 SVN_ERR(svn_sqlite__bindf(stmt, "sd",
7364 local_relpath, relpath_depth(local_relpath)));
7365 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7368 svn_wc__db_revert_list_copied_child_info_t *child_info;
7369 const char *child_relpath;
7371 child_info = apr_palloc(result_pool, sizeof(*child_info));
7373 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7374 child_info->abspath = svn_dirent_join(wcroot->abspath, child_relpath,
7376 child_info->kind = svn_sqlite__column_token(stmt, 1, kind_map);
7379 svn_wc__db_revert_list_copied_child_info_t *) = child_info;
7381 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7383 SVN_ERR(svn_sqlite__reset(stmt));
7385 *children_p = children;
7387 return SVN_NO_ERROR;
7392 svn_wc__db_revert_list_read_copied_children(apr_array_header_t **children,
7394 const char *local_abspath,
7395 apr_pool_t *result_pool,
7396 apr_pool_t *scratch_pool)
7398 svn_wc__db_wcroot_t *wcroot;
7399 const char *local_relpath;
7401 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7402 db, local_abspath, scratch_pool, scratch_pool));
7403 VERIFY_USABLE_WCROOT(wcroot);
7405 SVN_WC__DB_WITH_TXN(
7406 revert_list_read_copied_children(wcroot, local_relpath, children,
7407 result_pool, scratch_pool),
7409 return SVN_NO_ERROR;
7414 svn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func,
7417 const char *local_abspath,
7418 apr_pool_t *scratch_pool)
7420 svn_wc__db_wcroot_t *wcroot;
7421 const char *local_relpath;
7422 svn_sqlite__stmt_t *stmt;
7423 svn_boolean_t have_row;
7424 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
7426 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7427 db, local_abspath, scratch_pool, iterpool));
7428 VERIFY_USABLE_WCROOT(wcroot);
7430 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7431 STMT_SELECT_REVERT_LIST_RECURSIVE));
7432 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7433 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7435 return svn_error_trace(svn_sqlite__reset(stmt)); /* optimise for no row */
7438 const char *notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7439 svn_wc_notify_t *notify;
7441 svn_pool_clear(iterpool);
7443 notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
7446 svn_wc_notify_revert,
7449 if (!svn_sqlite__column_is_null(stmt, 1))
7450 notify->kind = svn_sqlite__column_token(stmt, 1, kind_map);
7453 if (!svn_sqlite__column_is_null(stmt, 3))
7454 notify->kind = svn_sqlite__column_token(stmt, 3, kind_map_none);
7456 switch (svn_sqlite__column_int(stmt, 2))
7461 /* standard revert */
7464 notify->action = svn_wc_notify_tree_conflict;
7467 SVN_ERR_MALFUNCTION();
7471 notify_func(notify_baton, notify, iterpool);
7473 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7475 SVN_ERR(svn_sqlite__reset(stmt));
7477 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7478 STMT_DELETE_REVERT_LIST_RECURSIVE));
7479 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7480 SVN_ERR(svn_sqlite__step_done(stmt));
7482 svn_pool_destroy(iterpool);
7484 return SVN_NO_ERROR;
7488 svn_wc__db_revert_list_done(svn_wc__db_t *db,
7489 const char *local_abspath,
7490 apr_pool_t *scratch_pool)
7492 svn_wc__db_wcroot_t *wcroot;
7493 const char *local_relpath;
7495 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7496 db, local_abspath, scratch_pool, scratch_pool));
7497 VERIFY_USABLE_WCROOT(wcroot);
7499 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_DROP_REVERT_LIST));
7501 return SVN_NO_ERROR;
7504 /* The body of svn_wc__db_op_remove_node().
7506 static svn_error_t *
7507 remove_node_txn(svn_boolean_t *left_changes,
7508 svn_wc__db_wcroot_t *wcroot,
7509 const char *local_relpath,
7511 svn_boolean_t destroy_wc,
7512 svn_boolean_t destroy_changes,
7513 const svn_skel_t *conflict,
7514 const svn_skel_t *work_items,
7515 svn_cancel_func_t cancel_func,
7517 apr_pool_t *scratch_pool)
7519 svn_sqlite__stmt_t *stmt;
7521 /* Note that unlike many similar functions it is a valid scenario for this
7522 function to be called on a wcroot! */
7524 /* db set when destroying wc */
7525 SVN_ERR_ASSERT(!destroy_wc || db != NULL);
7528 *left_changes = FALSE;
7531 && (!destroy_changes || *local_relpath == '\0'))
7533 svn_boolean_t have_row;
7534 apr_pool_t *iterpool;
7535 svn_error_t *err = NULL;
7537 /* Install WQ items for deleting the unmodified files and all dirs */
7538 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7539 STMT_SELECT_WORKING_PRESENT));
7540 SVN_ERR(svn_sqlite__bindf(stmt, "is",
7541 wcroot->wc_id, local_relpath));
7543 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7545 iterpool = svn_pool_create(scratch_pool);
7549 const char *child_relpath;
7550 const char *child_abspath;
7551 svn_node_kind_t child_kind;
7552 svn_boolean_t have_checksum;
7553 svn_filesize_t recorded_size;
7554 apr_int64_t recorded_time;
7555 const svn_io_dirent2_t *dirent;
7556 svn_boolean_t modified_p = TRUE;
7557 svn_skel_t *work_item = NULL;
7559 svn_pool_clear(iterpool);
7561 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7562 child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
7564 child_abspath = svn_dirent_join(wcroot->abspath, child_relpath,
7567 if (child_kind == svn_node_file)
7569 have_checksum = !svn_sqlite__column_is_null(stmt, 2);
7570 recorded_size = get_recorded_size(stmt, 3);
7571 recorded_time = svn_sqlite__column_int64(stmt, 4);
7575 err = cancel_func(cancel_baton);
7580 err = svn_io_stat_dirent2(&dirent, child_abspath, FALSE, TRUE,
7581 iterpool, iterpool);
7587 || dirent->kind != svn_node_file
7588 || child_kind != svn_node_file)
7590 /* Not interested in keeping changes */
7593 else if (child_kind == svn_node_file
7594 && dirent->kind == svn_node_file
7595 && dirent->filesize == recorded_size
7596 && dirent->mtime == recorded_time)
7598 modified_p = FALSE; /* File matches recorded state */
7600 else if (have_checksum)
7601 err = svn_wc__internal_file_modified_p(&modified_p,
7611 *left_changes = TRUE;
7613 else if (child_kind == svn_node_dir)
7615 err = svn_wc__wq_build_dir_remove(&work_item,
7616 db, wcroot->abspath,
7617 child_abspath, FALSE,
7618 iterpool, iterpool);
7620 else /* svn_node_file || svn_node_symlink */
7622 err = svn_wc__wq_build_file_remove(&work_item,
7623 db, wcroot->abspath,
7625 iterpool, iterpool);
7633 err = add_work_items(wcroot->sdb, work_item, iterpool);
7638 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7640 svn_pool_destroy(iterpool);
7642 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
7645 if (destroy_wc && *local_relpath != '\0')
7647 /* Create work item for destroying the root */
7648 svn_wc__db_status_t status;
7649 svn_node_kind_t kind;
7650 SVN_ERR(read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, NULL,
7651 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7652 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7653 wcroot, local_relpath,
7654 scratch_pool, scratch_pool));
7656 if (status == svn_wc__db_status_normal
7657 || status == svn_wc__db_status_added
7658 || status == svn_wc__db_status_incomplete)
7660 svn_skel_t *work_item = NULL;
7661 const char *local_abspath = svn_dirent_join(wcroot->abspath,
7665 if (kind == svn_node_dir)
7667 SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
7668 db, wcroot->abspath,
7672 scratch_pool, scratch_pool));
7676 svn_boolean_t modified_p = FALSE;
7678 if (!destroy_changes)
7680 SVN_ERR(svn_wc__internal_file_modified_p(&modified_p,
7687 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
7688 db, wcroot->abspath,
7695 *left_changes = TRUE;
7699 SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
7703 /* Remove all nodes below local_relpath */
7704 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7705 STMT_DELETE_NODE_RECURSIVE));
7706 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7707 SVN_ERR(svn_sqlite__step_done(stmt));
7709 /* Delete the root NODE when this is not the working copy root */
7710 if (local_relpath[0] != '\0')
7712 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7713 STMT_DELETE_NODE_ALL_LAYERS));
7714 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7715 SVN_ERR(svn_sqlite__step_done(stmt));
7718 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7719 STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7721 /* Delete all actual nodes at or below local_relpath */
7722 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7724 SVN_ERR(svn_sqlite__step_done(stmt));
7726 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
7728 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
7729 conflict, scratch_pool));
7731 return SVN_NO_ERROR;
7735 svn_wc__db_op_remove_node(svn_boolean_t *left_changes,
7737 const char *local_abspath,
7738 svn_boolean_t destroy_wc,
7739 svn_boolean_t destroy_changes,
7740 const svn_skel_t *conflict,
7741 const svn_skel_t *work_items,
7742 svn_cancel_func_t cancel_func,
7744 apr_pool_t *scratch_pool)
7746 svn_wc__db_wcroot_t *wcroot;
7747 const char *local_relpath;
7749 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7751 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7752 local_abspath, scratch_pool, scratch_pool));
7753 VERIFY_USABLE_WCROOT(wcroot);
7755 SVN_WC__DB_WITH_TXN(remove_node_txn(left_changes,
7756 wcroot, local_relpath, db,
7757 destroy_wc, destroy_changes,
7758 conflict, work_items,
7759 cancel_func, cancel_baton, scratch_pool),
7762 /* Flush everything below this node in all ways */
7763 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
7766 return SVN_NO_ERROR;
7770 /* The body of svn_wc__db_op_set_base_depth().
7772 static svn_error_t *
7773 db_op_set_base_depth(svn_wc__db_wcroot_t *wcroot,
7774 const char *local_relpath,
7776 apr_pool_t *scratch_pool)
7778 svn_sqlite__stmt_t *stmt;
7781 /* Flush any entries before we start monkeying the database. */
7782 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7783 STMT_UPDATE_NODE_BASE_DEPTH));
7784 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
7785 svn_token__to_word(depth_map, depth)));
7786 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7788 if (affected_rows == 0)
7789 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7790 _("The node '%s' is not a committed directory"),
7791 path_for_error_message(wcroot, local_relpath,
7794 return SVN_NO_ERROR;
7799 svn_wc__db_op_set_base_depth(svn_wc__db_t *db,
7800 const char *local_abspath,
7802 apr_pool_t *scratch_pool)
7804 svn_wc__db_wcroot_t *wcroot;
7805 const char *local_relpath;
7807 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7808 SVN_ERR_ASSERT(depth >= svn_depth_empty && depth <= svn_depth_infinity);
7810 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7811 local_abspath, scratch_pool, scratch_pool));
7812 VERIFY_USABLE_WCROOT(wcroot);
7814 /* ### We set depth on working and base to match entry behavior.
7815 Maybe these should be separated later? */
7816 SVN_WC__DB_WITH_TXN(db_op_set_base_depth(wcroot, local_relpath, depth,
7820 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
7822 return SVN_NO_ERROR;
7826 static svn_error_t *
7827 info_below_working(svn_boolean_t *have_base,
7828 svn_boolean_t *have_work,
7829 svn_wc__db_status_t *status,
7830 svn_wc__db_wcroot_t *wcroot,
7831 const char *local_relpath,
7832 int below_op_depth, /* < 0 is ignored */
7833 apr_pool_t *scratch_pool);
7836 /* Convert STATUS, the raw status obtained from the presence map, to
7837 the status appropriate for a working (op_depth > 0) node and return
7838 it in *WORKING_STATUS. */
7839 static svn_error_t *
7840 convert_to_working_status(svn_wc__db_status_t *working_status,
7841 svn_wc__db_status_t status)
7843 svn_wc__db_status_t work_status = status;
7845 SVN_ERR_ASSERT(work_status == svn_wc__db_status_normal
7846 || work_status == svn_wc__db_status_not_present
7847 || work_status == svn_wc__db_status_base_deleted
7848 || work_status == svn_wc__db_status_incomplete
7849 || work_status == svn_wc__db_status_excluded);
7851 if (work_status == svn_wc__db_status_excluded)
7853 *working_status = svn_wc__db_status_excluded;
7855 else if (work_status == svn_wc__db_status_not_present
7856 || work_status == svn_wc__db_status_base_deleted)
7858 /* The caller should scan upwards to detect whether this
7859 deletion has occurred because this node has been moved
7860 away, or it is a regular deletion. Also note that the
7861 deletion could be of the BASE tree, or a child of
7862 something that has been copied/moved here. */
7864 *working_status = svn_wc__db_status_deleted;
7866 else /* normal or incomplete */
7868 /* The caller should scan upwards to detect whether this
7869 addition has occurred because of a simple addition,
7870 a copy, or is the destination of a move. */
7871 *working_status = svn_wc__db_status_added;
7874 return SVN_NO_ERROR;
7878 /* Return the status of the node, if any, below the "working" node (or
7879 below BELOW_OP_DEPTH if >= 0).
7880 Set *HAVE_BASE or *HAVE_WORK to indicate if a base node or lower
7881 working node is present, and *STATUS to the status of the first
7882 layer below the selected node. */
7883 static svn_error_t *
7884 info_below_working(svn_boolean_t *have_base,
7885 svn_boolean_t *have_work,
7886 svn_wc__db_status_t *status,
7887 svn_wc__db_wcroot_t *wcroot,
7888 const char *local_relpath,
7890 apr_pool_t *scratch_pool)
7892 svn_sqlite__stmt_t *stmt;
7893 svn_boolean_t have_row;
7895 *have_base = *have_work = FALSE;
7896 *status = svn_wc__db_status_normal;
7898 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7899 STMT_SELECT_NODE_INFO));
7900 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7901 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7903 if (below_op_depth >= 0)
7906 (svn_sqlite__column_int(stmt, 0) > below_op_depth))
7908 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7913 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7915 *status = svn_sqlite__column_token(stmt, 3, presence_map);
7919 int op_depth = svn_sqlite__column_int(stmt, 0);
7926 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7929 SVN_ERR(svn_sqlite__reset(stmt));
7932 SVN_ERR(convert_to_working_status(status, *status));
7934 return SVN_NO_ERROR;
7937 /* Helper function for op_delete_txn */
7938 static svn_error_t *
7939 delete_update_movedto(svn_wc__db_wcroot_t *wcroot,
7940 const char *child_moved_from_relpath,
7942 const char *new_moved_to_relpath,
7943 apr_pool_t *scratch_pool)
7945 svn_sqlite__stmt_t *stmt;
7948 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7949 STMT_UPDATE_MOVED_TO_RELPATH));
7951 SVN_ERR(svn_sqlite__bindf(stmt, "isds",
7953 child_moved_from_relpath,
7955 new_moved_to_relpath));
7956 SVN_ERR(svn_sqlite__update(&affected, stmt));
7958 /* Not fatal in release mode. The move recording is broken,
7959 but the rest of the working copy can handle this. */
7960 SVN_ERR_ASSERT(affected == 1);
7963 return SVN_NO_ERROR;
7967 struct op_delete_baton_t {
7968 const char *moved_to_relpath; /* NULL if delete is not part of a move */
7969 svn_skel_t *conflict;
7970 svn_skel_t *work_items;
7971 svn_boolean_t delete_dir_externals;
7972 svn_boolean_t notify;
7975 /* This structure is used while rewriting move information for nodes.
7977 * The most simple case of rewriting move information happens when
7978 * a moved-away subtree is moved again: mv A B; mv B C
7979 * The second move requires rewriting moved-to info at or within A.
7981 * Another example is a move of a subtree which had nodes moved into it:
7983 * This requires rewriting such that A/F is marked has having moved to G/F.
7985 * Another case is where a node becomes a nested moved node.
7986 * A nested move happens when a subtree child is moved before or after
7987 * the subtree itself is moved. For example:
7988 * mv A/F A/G; mv A B
7989 * In this case, the move A/F -> A/G is rewritten to B/F -> B/G.
7990 * Note that the following sequence results in the same DB state:
7991 * mv A B; mv B/F B/G
7992 * We do not care about the order the moves were performed in.
7993 * For details, see http://wiki.apache.org/subversion/MultiLayerMoves
7995 struct moved_node_t {
7996 /* The source of the move. */
7997 const char *local_relpath;
7999 /* The move destination. */
8000 const char *moved_to_relpath;
8002 /* The op-depth of the deleted node at the source of the move. */
8005 /* When >= 1 the op_depth at which local_relpath was moved to its
8006 location. Used to find its original location outside the delete */
8007 int moved_from_depth;
8010 /* Helper function to resolve the original location of local_relpath at OP_DEPTH
8011 before it was moved into the tree rooted at ROOT_RELPATH. */
8012 static svn_error_t *
8013 resolve_moved_from(const char **moved_from_relpath,
8014 int *moved_from_op_depth,
8015 svn_wc__db_wcroot_t *wcroot,
8016 const char *root_relpath,
8017 const char *local_relpath,
8019 apr_pool_t *result_pool,
8020 apr_pool_t *scratch_pool)
8022 const char *suffix = "";
8023 svn_sqlite__stmt_t *stmt;
8024 const char *m_from_relpath;
8025 int m_from_op_depth;
8026 int m_move_from_depth;
8027 svn_boolean_t have_row;
8029 while (relpath_depth(local_relpath) > op_depth)
8032 svn_relpath_split(&local_relpath, &name, local_relpath, scratch_pool);
8033 suffix = svn_relpath_join(suffix, name, scratch_pool);
8036 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8037 STMT_SELECT_MOVED_FROM_FOR_DELETE));
8038 SVN_ERR(svn_sqlite__bindf(stmt, "is",
8039 wcroot->wc_id, local_relpath));
8040 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8044 /* assert(have_row); */
8045 *moved_from_relpath = NULL;
8046 *moved_from_op_depth = -1;
8048 SVN_ERR(svn_sqlite__reset(stmt));
8050 return SVN_NO_ERROR;
8053 m_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
8054 m_from_op_depth = svn_sqlite__column_int(stmt, 1);
8055 m_move_from_depth = svn_sqlite__column_int(stmt, 2);
8057 SVN_ERR(svn_sqlite__reset(stmt));
8059 if (! svn_relpath_skip_ancestor(root_relpath, m_from_relpath))
8061 *moved_from_relpath = svn_relpath_join(m_from_relpath, suffix,
8063 *moved_from_op_depth = m_from_op_depth; /* ### Ok? */
8064 return SVN_NO_ERROR;
8066 else if (!m_move_from_depth)
8068 *moved_from_relpath = NULL;
8069 *moved_from_op_depth = -1;
8070 return SVN_NO_ERROR;
8073 return svn_error_trace(
8074 resolve_moved_from(moved_from_relpath,
8075 moved_from_op_depth,
8078 svn_relpath_join(m_from_relpath, suffix,
8081 result_pool, scratch_pool));
8084 static svn_error_t *
8085 delete_node(void *baton,
8086 svn_wc__db_wcroot_t *wcroot,
8087 const char *local_relpath,
8088 apr_pool_t *scratch_pool)
8090 struct op_delete_baton_t *b = baton;
8091 svn_wc__db_status_t status;
8092 svn_boolean_t have_row, op_root;
8093 svn_boolean_t add_work = FALSE;
8094 svn_sqlite__stmt_t *stmt;
8095 int working_op_depth; /* Depth of what is to be deleted */
8096 int keep_op_depth = 0; /* Depth of what is below what is deleted */
8097 svn_node_kind_t kind;
8098 apr_array_header_t *moved_nodes = NULL;
8099 int delete_op_depth = relpath_depth(local_relpath);
8101 assert(*local_relpath); /* Can't delete wcroot */
8103 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8104 STMT_SELECT_NODE_INFO));
8105 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8106 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8110 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
8111 svn_sqlite__reset(stmt),
8112 _("The node '%s' was not found."),
8113 path_for_error_message(wcroot,
8118 working_op_depth = svn_sqlite__column_int(stmt, 0);
8119 status = svn_sqlite__column_token(stmt, 3, presence_map);
8120 kind = svn_sqlite__column_token(stmt, 4, kind_map);
8122 if (working_op_depth < delete_op_depth)
8126 keep_op_depth = working_op_depth;
8132 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8136 svn_wc__db_status_t below_status;
8139 below_op_depth = svn_sqlite__column_int(stmt, 0);
8140 below_status = svn_sqlite__column_token(stmt, 3, presence_map);
8142 if (below_status != svn_wc__db_status_not_present
8143 && below_status != svn_wc__db_status_base_deleted)
8146 keep_op_depth = below_op_depth;
8155 SVN_ERR(svn_sqlite__reset(stmt));
8157 if (working_op_depth != 0) /* WORKING */
8158 SVN_ERR(convert_to_working_status(&status, status));
8160 if (status == svn_wc__db_status_deleted
8161 || status == svn_wc__db_status_not_present)
8162 return SVN_NO_ERROR;
8164 /* Don't copy BASE directories with server excluded nodes */
8165 if (status == svn_wc__db_status_normal && kind == svn_node_dir)
8167 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8168 STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
8169 SVN_ERR(svn_sqlite__bindf(stmt, "is",
8170 wcroot->wc_id, local_relpath));
8171 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8174 const char *absent_path = svn_sqlite__column_text(stmt, 0,
8177 return svn_error_createf(
8178 SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
8179 svn_sqlite__reset(stmt),
8180 _("Cannot delete '%s' as '%s' is excluded by server"),
8181 path_for_error_message(wcroot, local_relpath,
8183 path_for_error_message(wcroot, absent_path,
8186 SVN_ERR(svn_sqlite__reset(stmt));
8188 else if (status == svn_wc__db_status_server_excluded)
8190 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
8191 _("Cannot delete '%s' as it is excluded by server"),
8192 path_for_error_message(wcroot, local_relpath,
8195 else if (status == svn_wc__db_status_excluded)
8197 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
8198 _("Cannot delete '%s' as it is excluded"),
8199 path_for_error_message(wcroot, local_relpath,
8203 if (b->moved_to_relpath)
8205 const char *moved_from_relpath = NULL;
8206 struct moved_node_t *moved_node;
8209 moved_nodes = apr_array_make(scratch_pool, 1,
8210 sizeof(struct moved_node_t *));
8212 /* The node is being moved-away.
8213 * Figure out if the node was moved-here before, or whether this
8214 * is the first time the node is moved. */
8215 if (status == svn_wc__db_status_added)
8216 SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL,
8217 &moved_from_relpath,
8220 wcroot, local_relpath,
8221 scratch_pool, scratch_pool));
8223 if (op_root && moved_from_relpath)
8225 const char *part = svn_relpath_skip_ancestor(local_relpath,
8226 moved_from_relpath);
8228 /* Existing move-root is moved to another location */
8229 moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
8231 moved_node->local_relpath = moved_from_relpath;
8233 moved_node->local_relpath = svn_relpath_join(b->moved_to_relpath,
8234 part, scratch_pool);
8235 moved_node->op_depth = move_op_depth;
8236 moved_node->moved_to_relpath = b->moved_to_relpath;
8237 moved_node->moved_from_depth = -1;
8239 APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
8241 else if (!op_root && (status == svn_wc__db_status_normal
8242 || status == svn_wc__db_status_copied
8243 || status == svn_wc__db_status_moved_here))
8245 /* The node is becoming a move-root for the first time,
8246 * possibly because of a nested move operation. */
8247 moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
8248 moved_node->local_relpath = local_relpath;
8249 moved_node->op_depth = delete_op_depth;
8250 moved_node->moved_to_relpath = b->moved_to_relpath;
8251 moved_node->moved_from_depth = -1;
8253 APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
8255 /* Else: We can't track history of local additions and/or of things we are
8258 /* And update all moved_to values still pointing to this location */
8259 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8260 STMT_UPDATE_MOVED_TO_DESCENDANTS));
8261 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
8263 b->moved_to_relpath));
8264 SVN_ERR(svn_sqlite__update(NULL, stmt));
8267 /* Find children that were moved out of the subtree rooted at this node.
8268 * We'll need to update their op-depth columns because their deletion
8269 * is now implied by the deletion of their parent (i.e. this node). */
8271 apr_pool_t *iterpool;
8274 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8275 STMT_SELECT_MOVED_FOR_DELETE));
8276 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
8279 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8280 iterpool = svn_pool_create(scratch_pool);
8283 struct moved_node_t *mn;
8284 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8285 const char *mv_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
8286 int child_op_depth = svn_sqlite__column_int(stmt, 2);
8287 int moved_from_depth = -1;
8288 svn_boolean_t fixup = FALSE;
8290 if (! b->moved_to_relpath
8291 && ! svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
8293 /* a NULL moved_here_depth will be reported as 0 */
8294 int moved_here_depth = svn_sqlite__column_int(stmt, 3);
8296 /* Plain delete. Fixup move information of descendants that were
8297 moved here, or that were moved out */
8299 if (moved_here_depth >= delete_op_depth)
8301 /* The move we recorded here must be moved to the location
8302 this node had before it was moved here.
8304 This might contain multiple steps when the node was moved
8305 in several places within the to be deleted tree */
8307 /* ### TODO: Add logic */
8309 moved_from_depth = moved_here_depth;
8313 /* Update the op-depth of an moved away node that was
8314 registered as moved by the records that we are about
8317 child_op_depth = delete_op_depth;
8320 else if (b->moved_to_relpath)
8322 /* The node is moved to a new location */
8324 if (delete_op_depth == child_op_depth)
8326 /* Update the op-depth of a tree shadowed by this tree */
8328 /*child_op_depth = delete_depth;*/
8330 else if (child_op_depth >= delete_op_depth
8331 && !svn_relpath_skip_ancestor(local_relpath,
8334 /* Update the move destination of something that is now moved
8337 child_relpath = svn_relpath_skip_ancestor(local_relpath,
8342 child_relpath = svn_relpath_join(b->moved_to_relpath,
8346 if (child_op_depth > delete_op_depth
8347 && svn_relpath_skip_ancestor(local_relpath,
8349 child_op_depth = delete_op_depth;
8352 /* Calculate depth of the shadowing at the new location */
8353 child_op_depth = child_op_depth
8354 - relpath_depth(local_relpath)
8355 + relpath_depth(b->moved_to_relpath);
8365 mn = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
8367 mn->local_relpath = apr_pstrdup(scratch_pool, child_relpath);
8368 mn->moved_to_relpath = apr_pstrdup(scratch_pool, mv_to_relpath);
8369 mn->op_depth = child_op_depth;
8370 mn->moved_from_depth = moved_from_depth;
8373 moved_nodes = apr_array_make(scratch_pool, 1,
8374 sizeof(struct moved_node_t *));
8375 APR_ARRAY_PUSH(moved_nodes, struct moved_node_t *) = mn;
8378 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8380 SVN_ERR(svn_sqlite__reset(stmt));
8382 for (i = 0; moved_nodes && (i < moved_nodes->nelts); i++)
8384 struct moved_node_t *mn = APR_ARRAY_IDX(moved_nodes, i,
8385 struct moved_node_t *);
8387 if (mn->moved_from_depth > 0)
8389 svn_pool_clear(iterpool);
8391 SVN_ERR(resolve_moved_from(&mn->local_relpath, &mn->op_depth,
8392 wcroot, local_relpath,
8394 mn->moved_from_depth,
8395 scratch_pool, iterpool));
8397 if (!mn->local_relpath)
8398 svn_sort__array_delete(moved_nodes, i--, 1);
8402 svn_pool_destroy(iterpool);
8405 if (!b->moved_to_relpath)
8407 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8408 STMT_CLEAR_MOVED_TO_DESCENDANTS));
8409 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8411 SVN_ERR(svn_sqlite__update(NULL, stmt));
8415 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8416 STMT_CLEAR_MOVED_TO_FROM_DEST));
8417 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8420 SVN_ERR(svn_sqlite__update(NULL, stmt));
8425 /* ### Put actual-only nodes into the list? */
8428 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8429 STMT_INSERT_DELETE_LIST));
8430 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
8431 wcroot->wc_id, local_relpath, working_op_depth));
8432 SVN_ERR(svn_sqlite__step_done(stmt));
8435 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8436 STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
8437 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
8438 wcroot->wc_id, local_relpath, delete_op_depth));
8439 SVN_ERR(svn_sqlite__step_done(stmt));
8441 /* Delete ACTUAL_NODE rows, but leave those that have changelist
8443 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8444 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8445 SVN_ERR(svn_sqlite__bindf(stmt, "is",
8446 wcroot->wc_id, local_relpath));
8447 SVN_ERR(svn_sqlite__step_done(stmt));
8449 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8450 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8451 SVN_ERR(svn_sqlite__bindf(stmt, "is",
8452 wcroot->wc_id, local_relpath));
8453 SVN_ERR(svn_sqlite__step_done(stmt));
8455 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8456 STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
8457 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8459 SVN_ERR(svn_sqlite__step_done(stmt));
8463 /* Delete the node at LOCAL_RELPATH, and possibly mark it as moved. */
8465 /* Delete the node and possible descendants. */
8466 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8467 STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
8468 SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
8469 wcroot->wc_id, local_relpath,
8470 keep_op_depth, delete_op_depth));
8471 SVN_ERR(svn_sqlite__step_done(stmt));
8478 for (i = 0; i < moved_nodes->nelts; ++i)
8480 const struct moved_node_t *moved_node
8481 = APR_ARRAY_IDX(moved_nodes, i, void *);
8483 SVN_ERR(delete_update_movedto(wcroot,
8484 moved_node->local_relpath,
8485 moved_node->op_depth,
8486 moved_node->moved_to_relpath,
8491 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8492 STMT_DELETE_FILE_EXTERNALS));
8493 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8494 SVN_ERR(svn_sqlite__step_done(stmt));
8496 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8497 b->delete_dir_externals
8498 ? STMT_DELETE_EXTERNAL_REGISTATIONS
8499 : STMT_DELETE_FILE_EXTERNAL_REGISTATIONS));
8500 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8501 SVN_ERR(svn_sqlite__step_done(stmt));
8503 SVN_ERR(add_work_items(wcroot->sdb, b->work_items, scratch_pool));
8505 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
8506 b->conflict, scratch_pool));
8508 return SVN_NO_ERROR;
8511 static svn_error_t *
8512 op_delete_txn(void *baton,
8513 svn_wc__db_wcroot_t *wcroot,
8514 const char *local_relpath,
8515 apr_pool_t *scratch_pool)
8518 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8519 SVN_ERR(delete_node(baton, wcroot, local_relpath, scratch_pool));
8520 return SVN_NO_ERROR;
8524 struct op_delete_many_baton_t {
8525 apr_array_header_t *rel_targets;
8526 svn_boolean_t delete_dir_externals;
8527 const svn_skel_t *work_items;
8530 static svn_error_t *
8531 op_delete_many_txn(void *baton,
8532 svn_wc__db_wcroot_t *wcroot,
8533 const char *local_relpath,
8534 apr_pool_t *scratch_pool)
8536 struct op_delete_many_baton_t *odmb = baton;
8537 struct op_delete_baton_t odb;
8539 apr_pool_t *iterpool;
8541 odb.moved_to_relpath = NULL;
8542 odb.conflict = NULL;
8543 odb.work_items = NULL;
8544 odb.delete_dir_externals = odmb->delete_dir_externals;
8547 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8548 iterpool = svn_pool_create(scratch_pool);
8549 for (i = 0; i < odmb->rel_targets->nelts; i++)
8551 const char *target_relpath = APR_ARRAY_IDX(odmb->rel_targets, i,
8555 svn_pool_clear(iterpool);
8556 SVN_ERR(delete_node(&odb, wcroot, target_relpath, iterpool));
8558 svn_pool_destroy(iterpool);
8560 SVN_ERR(add_work_items(wcroot->sdb, odmb->work_items, scratch_pool));
8562 return SVN_NO_ERROR;
8566 static svn_error_t *
8567 do_delete_notify(void *baton,
8568 svn_wc__db_wcroot_t *wcroot,
8569 svn_cancel_func_t cancel_func,
8571 svn_wc_notify_func2_t notify_func,
8573 apr_pool_t *scratch_pool)
8575 svn_sqlite__stmt_t *stmt;
8576 svn_boolean_t have_row;
8577 apr_pool_t *iterpool;
8579 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8580 STMT_SELECT_DELETE_LIST));
8581 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8583 iterpool = svn_pool_create(scratch_pool);
8586 const char *notify_relpath;
8587 const char *notify_abspath;
8589 svn_pool_clear(iterpool);
8591 notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8592 notify_abspath = svn_dirent_join(wcroot->abspath,
8596 notify_func(notify_baton,
8597 svn_wc_create_notify(notify_abspath,
8598 svn_wc_notify_delete,
8602 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8604 svn_pool_destroy(iterpool);
8606 SVN_ERR(svn_sqlite__reset(stmt));
8608 /* We only allow cancellation after notification for all deleted nodes
8609 * has happened. The nodes are already deleted so we should notify for
8612 SVN_ERR(cancel_func(cancel_baton));
8614 return SVN_NO_ERROR;
8619 svn_wc__db_op_delete(svn_wc__db_t *db,
8620 const char *local_abspath,
8621 const char *moved_to_abspath,
8622 svn_boolean_t delete_dir_externals,
8623 svn_skel_t *conflict,
8624 svn_skel_t *work_items,
8625 svn_cancel_func_t cancel_func,
8627 svn_wc_notify_func2_t notify_func,
8629 apr_pool_t *scratch_pool)
8631 svn_wc__db_wcroot_t *wcroot;
8632 svn_wc__db_wcroot_t *moved_to_wcroot;
8633 const char *local_relpath;
8634 const char *moved_to_relpath;
8635 struct op_delete_baton_t odb;
8637 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8639 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8641 scratch_pool, scratch_pool));
8642 VERIFY_USABLE_WCROOT(wcroot);
8644 if (moved_to_abspath)
8646 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&moved_to_wcroot,
8648 db, moved_to_abspath,
8651 VERIFY_USABLE_WCROOT(moved_to_wcroot);
8653 if (strcmp(wcroot->abspath, moved_to_wcroot->abspath) != 0)
8654 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
8655 _("Cannot move '%s' to '%s' because they "
8656 "are not in the same working copy"),
8657 svn_dirent_local_style(local_abspath,
8659 svn_dirent_local_style(moved_to_abspath,
8663 moved_to_relpath = NULL;
8665 odb.moved_to_relpath = moved_to_relpath;
8666 odb.conflict = conflict;
8667 odb.work_items = work_items;
8668 odb.delete_dir_externals = delete_dir_externals;
8672 /* Perform the deletion operation (transactionally), perform any
8673 notifications necessary, and then clean out our temporary tables. */
8675 SVN_ERR(with_finalization(wcroot, local_relpath,
8676 op_delete_txn, &odb,
8677 do_delete_notify, NULL,
8678 cancel_func, cancel_baton,
8679 notify_func, notify_baton,
8680 STMT_FINALIZE_DELETE,
8685 /* Avoid the trigger work */
8687 SVN_WC__DB_WITH_TXN(
8688 delete_node(&odb, wcroot, local_relpath, scratch_pool),
8692 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
8695 return SVN_NO_ERROR;
8700 svn_wc__db_op_delete_many(svn_wc__db_t *db,
8701 apr_array_header_t *targets,
8702 svn_boolean_t delete_dir_externals,
8703 const svn_skel_t *work_items,
8704 svn_cancel_func_t cancel_func,
8706 svn_wc_notify_func2_t notify_func,
8708 apr_pool_t *scratch_pool)
8710 svn_wc__db_wcroot_t *wcroot;
8711 const char *local_relpath;
8712 struct op_delete_many_baton_t odmb;
8714 apr_pool_t *iterpool;
8716 odmb.rel_targets = apr_array_make(scratch_pool, targets->nelts,
8717 sizeof(const char *));
8718 odmb.work_items = work_items;
8719 odmb.delete_dir_externals = delete_dir_externals;
8720 iterpool = svn_pool_create(scratch_pool);
8721 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8723 APR_ARRAY_IDX(targets, 0,
8725 scratch_pool, iterpool));
8726 VERIFY_USABLE_WCROOT(wcroot);
8727 for (i = 0; i < targets->nelts; i++)
8729 const char *local_abspath = APR_ARRAY_IDX(targets, i, const char*);
8730 svn_wc__db_wcroot_t *target_wcroot;
8732 svn_pool_clear(iterpool);
8734 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&target_wcroot,
8736 APR_ARRAY_IDX(targets, i,
8738 scratch_pool, iterpool));
8739 VERIFY_USABLE_WCROOT(target_wcroot);
8740 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8742 /* Assert that all targets are within the same working copy. */
8743 SVN_ERR_ASSERT(wcroot->wc_id == target_wcroot->wc_id);
8745 APR_ARRAY_PUSH(odmb.rel_targets, const char *) = local_relpath;
8746 SVN_ERR(flush_entries(target_wcroot, local_abspath, svn_depth_infinity,
8750 svn_pool_destroy(iterpool);
8752 /* Perform the deletion operation (transactionally), perform any
8753 notifications necessary, and then clean out our temporary tables. */
8754 return svn_error_trace(with_finalization(wcroot, wcroot->abspath,
8755 op_delete_many_txn, &odmb,
8756 do_delete_notify, NULL,
8757 cancel_func, cancel_baton,
8758 notify_func, notify_baton,
8759 STMT_FINALIZE_DELETE,
8763 /* Helper function for read_info() to provide better diagnostics than just
8766 ### BH: Yes this code is ugly, and that is why I only introduce it in
8767 ### read_info(). But we really need something to determine the root cause
8768 ### of this problem to diagnose why TortoiseSVN users were seeing all those
8771 Adds an error to the *err chain if invalid values are encountered. In that
8772 case the value is set to the first value in the map, assuming that caller
8773 will just return the combined error.
8776 column_token_err(svn_error_t **err,
8777 svn_sqlite__stmt_t *stmt,
8779 const svn_token_map_t *map)
8782 const char *word = svn_sqlite__column_text(stmt, column, NULL);
8785 /* svn_token__from_word_err() handles NULL for us */
8786 err2 = svn_token__from_word_err(&value, map, word);
8790 *err = svn_error_compose_create(
8793 SVN_ERR_WC_CORRUPT, err2,
8794 _("Encountered invalid node state in column %d of "
8795 "info query to working copy database"),
8803 /* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of
8804 DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */
8805 static svn_error_t *
8806 read_info(svn_wc__db_status_t *status,
8807 svn_node_kind_t *kind,
8808 svn_revnum_t *revision,
8809 const char **repos_relpath,
8810 apr_int64_t *repos_id,
8811 svn_revnum_t *changed_rev,
8812 apr_time_t *changed_date,
8813 const char **changed_author,
8815 const svn_checksum_t **checksum,
8816 const char **target,
8817 const char **original_repos_relpath,
8818 apr_int64_t *original_repos_id,
8819 svn_revnum_t *original_revision,
8820 svn_wc__db_lock_t **lock,
8821 svn_filesize_t *recorded_size,
8822 apr_time_t *recorded_time,
8823 const char **changelist,
8824 svn_boolean_t *conflicted,
8825 svn_boolean_t *op_root,
8826 svn_boolean_t *had_props,
8827 svn_boolean_t *props_mod,
8828 svn_boolean_t *have_base,
8829 svn_boolean_t *have_more_work,
8830 svn_boolean_t *have_work,
8831 svn_wc__db_wcroot_t *wcroot,
8832 const char *local_relpath,
8833 apr_pool_t *result_pool,
8834 apr_pool_t *scratch_pool)
8836 svn_sqlite__stmt_t *stmt_info;
8837 svn_sqlite__stmt_t *stmt_act;
8838 svn_boolean_t have_info;
8839 svn_boolean_t have_act;
8840 svn_error_t *err = NULL;
8842 /* Obtain the most likely to exist record first, to make sure we don't
8843 have to obtain the SQLite read-lock multiple times */
8844 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
8845 lock ? STMT_SELECT_NODE_INFO_WITH_LOCK
8846 : STMT_SELECT_NODE_INFO));
8847 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
8848 SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
8850 if (changelist || conflicted || props_mod)
8852 SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
8853 STMT_SELECT_ACTUAL_NODE));
8854 SVN_ERR(svn_sqlite__bindf(stmt_act, "is", wcroot->wc_id, local_relpath));
8855 SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
8866 svn_node_kind_t node_kind;
8868 op_depth = svn_sqlite__column_int(stmt_info, 0);
8869 node_kind = column_token_err(&err, stmt_info, 4, kind_map);
8873 *status = column_token_err(&err, stmt_info, 3, presence_map);
8875 if (op_depth != 0) /* WORKING */
8876 err = svn_error_compose_create(err,
8877 convert_to_working_status(status,
8887 *repos_id = INVALID_REPOS_ID;
8889 *revision = SVN_INVALID_REVNUM;
8891 /* Our path is implied by our parent somewhere up the tree.
8892 With the NULL value and status, the caller will know to
8893 search up the tree for the base of our path. */
8894 *repos_relpath = NULL;
8898 /* Fetch repository information. If we have a
8899 WORKING_NODE (and have been added), then the repository
8900 we're being added to will be dependent upon a parent. The
8901 caller can scan upwards to locate the repository. */
8902 repos_location_from_columns(repos_id, revision, repos_relpath,
8903 stmt_info, 1, 5, 2, result_pool);
8907 *changed_rev = svn_sqlite__column_revnum(stmt_info, 8);
8911 *changed_date = svn_sqlite__column_int64(stmt_info, 9);
8915 *changed_author = svn_sqlite__column_text(stmt_info, 10,
8920 *recorded_time = svn_sqlite__column_int64(stmt_info, 13);
8924 if (node_kind != svn_node_dir)
8925 *depth = svn_depth_unknown;
8926 else if (svn_sqlite__column_is_null(stmt_info, 11))
8927 *depth = svn_depth_unknown;
8929 *depth = column_token_err(&err, stmt_info, 11, depth_map);
8933 if (node_kind != svn_node_file)
8940 err = svn_error_compose_create(
8941 err, svn_sqlite__column_checksum(checksum, stmt_info, 6,
8947 *recorded_size = get_recorded_size(stmt_info, 7);
8951 if (node_kind != svn_node_symlink)
8954 *target = svn_sqlite__column_text(stmt_info, 12, result_pool);
8959 *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8965 if (original_repos_id)
8966 *original_repos_id = INVALID_REPOS_ID;
8967 if (original_revision)
8968 *original_revision = SVN_INVALID_REVNUM;
8969 if (original_repos_relpath)
8970 *original_repos_relpath = NULL;
8974 repos_location_from_columns(original_repos_id,
8976 original_repos_relpath,
8977 stmt_info, 1, 5, 2, result_pool);
8981 *props_mod = have_act && !svn_sqlite__column_is_null(stmt_act, 1);
8985 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt_info, 14);
8992 !svn_sqlite__column_is_null(stmt_act, 2); /* conflict_data */
8995 *conflicted = FALSE;
9003 *lock = lock_from_columns(stmt_info, 17, 18, 19, 20, result_pool);
9007 *have_work = (op_depth != 0);
9011 *op_root = ((op_depth > 0)
9012 && (op_depth == relpath_depth(local_relpath)));
9015 if (have_base || have_more_work)
9018 *have_more_work = FALSE;
9020 while (!err && op_depth != 0)
9022 err = svn_sqlite__step(&have_info, stmt_info);
9024 if (err || !have_info)
9027 op_depth = svn_sqlite__column_int(stmt_info, 0);
9032 *have_more_work = TRUE;
9040 *have_base = (op_depth == 0);
9045 /* A row in ACTUAL_NODE should never exist without a corresponding
9046 node in BASE_NODE and/or WORKING_NODE unless it flags a tree conflict. */
9047 if (svn_sqlite__column_is_null(stmt_act, 2)) /* conflict_data */
9048 err = svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
9049 _("Corrupt data for '%s'"),
9050 path_for_error_message(wcroot, local_relpath,
9052 /* ### What should we return? Should we have a separate
9053 function for reading actual-only nodes? */
9055 /* As a safety measure, until we decide if we want to use
9056 read_info for actual-only nodes, make sure the caller asked
9057 for the conflict status. */
9058 SVN_ERR_ASSERT(conflicted);
9061 *status = svn_wc__db_status_normal; /* What! No it's not! */
9063 *kind = svn_node_unknown;
9065 *revision = SVN_INVALID_REVNUM;
9067 *repos_relpath = NULL;
9069 *repos_id = INVALID_REPOS_ID;
9071 *changed_rev = SVN_INVALID_REVNUM;
9075 *depth = svn_depth_unknown;
9080 if (original_repos_relpath)
9081 *original_repos_relpath = NULL;
9082 if (original_repos_id)
9083 *original_repos_id = INVALID_REPOS_ID;
9084 if (original_revision)
9085 *original_revision = SVN_INVALID_REVNUM;
9093 *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
9105 *have_more_work = FALSE;
9111 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
9112 _("The node '%s' was not found."),
9113 path_for_error_message(wcroot, local_relpath,
9117 if (stmt_act != NULL)
9118 err = svn_error_compose_create(err, svn_sqlite__reset(stmt_act));
9120 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
9121 err = svn_error_quick_wrapf(err, _("Error reading node '%s'"),
9124 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info)));
9126 return SVN_NO_ERROR;
9131 svn_wc__db_read_info_internal(svn_wc__db_status_t *status,
9132 svn_node_kind_t *kind,
9133 svn_revnum_t *revision,
9134 const char **repos_relpath,
9135 apr_int64_t *repos_id,
9136 svn_revnum_t *changed_rev,
9137 apr_time_t *changed_date,
9138 const char **changed_author,
9140 const svn_checksum_t **checksum,
9141 const char **target,
9142 const char **original_repos_relpath,
9143 apr_int64_t *original_repos_id,
9144 svn_revnum_t *original_revision,
9145 svn_wc__db_lock_t **lock,
9146 svn_filesize_t *recorded_size,
9147 apr_time_t *recorded_time,
9148 const char **changelist,
9149 svn_boolean_t *conflicted,
9150 svn_boolean_t *op_root,
9151 svn_boolean_t *had_props,
9152 svn_boolean_t *props_mod,
9153 svn_boolean_t *have_base,
9154 svn_boolean_t *have_more_work,
9155 svn_boolean_t *have_work,
9156 svn_wc__db_wcroot_t *wcroot,
9157 const char *local_relpath,
9158 apr_pool_t *result_pool,
9159 apr_pool_t *scratch_pool)
9161 return svn_error_trace(
9162 read_info(status, kind, revision, repos_relpath, repos_id,
9163 changed_rev, changed_date, changed_author,
9164 depth, checksum, target, original_repos_relpath,
9165 original_repos_id, original_revision, lock,
9166 recorded_size, recorded_time, changelist, conflicted,
9167 op_root, had_props, props_mod,
9168 have_base, have_more_work, have_work,
9169 wcroot, local_relpath, result_pool, scratch_pool));
9174 svn_wc__db_read_info(svn_wc__db_status_t *status,
9175 svn_node_kind_t *kind,
9176 svn_revnum_t *revision,
9177 const char **repos_relpath,
9178 const char **repos_root_url,
9179 const char **repos_uuid,
9180 svn_revnum_t *changed_rev,
9181 apr_time_t *changed_date,
9182 const char **changed_author,
9184 const svn_checksum_t **checksum,
9185 const char **target,
9186 const char **original_repos_relpath,
9187 const char **original_root_url,
9188 const char **original_uuid,
9189 svn_revnum_t *original_revision,
9190 svn_wc__db_lock_t **lock,
9191 svn_filesize_t *recorded_size,
9192 apr_time_t *recorded_time,
9193 const char **changelist,
9194 svn_boolean_t *conflicted,
9195 svn_boolean_t *op_root,
9196 svn_boolean_t *have_props,
9197 svn_boolean_t *props_mod,
9198 svn_boolean_t *have_base,
9199 svn_boolean_t *have_more_work,
9200 svn_boolean_t *have_work,
9202 const char *local_abspath,
9203 apr_pool_t *result_pool,
9204 apr_pool_t *scratch_pool)
9206 svn_wc__db_wcroot_t *wcroot;
9207 const char *local_relpath;
9208 apr_int64_t repos_id, original_repos_id;
9210 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9212 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9213 local_abspath, scratch_pool, scratch_pool));
9214 VERIFY_USABLE_WCROOT(wcroot);
9216 SVN_WC__DB_WITH_TXN4(
9217 read_info(status, kind, revision, repos_relpath, &repos_id,
9218 changed_rev, changed_date, changed_author,
9219 depth, checksum, target, original_repos_relpath,
9220 &original_repos_id, original_revision, lock,
9221 recorded_size, recorded_time, changelist, conflicted,
9222 op_root, have_props, props_mod,
9223 have_base, have_more_work, have_work,
9224 wcroot, local_relpath, result_pool, scratch_pool),
9225 svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
9226 wcroot, repos_id, result_pool),
9227 svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
9228 wcroot, original_repos_id,
9233 return SVN_NO_ERROR;
9236 static svn_error_t *
9237 is_wclocked(svn_boolean_t *locked,
9238 svn_wc__db_wcroot_t *wcroot,
9239 const char *dir_relpath,
9240 apr_pool_t *scratch_pool);
9242 /* Helper for read_children_info and single variant */
9243 static svn_error_t *
9244 find_conflict_descendants(svn_boolean_t *conflict_exists,
9245 svn_wc__db_wcroot_t *wcroot,
9246 const char *local_relpath,
9247 apr_pool_t *scratch_pool)
9249 svn_sqlite__stmt_t *stmt;
9251 /* Only used on files, so certainly not wcroot*/
9252 assert(local_relpath[0] != '\0');
9254 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9255 STMT_FIND_CONFLICT_DESCENDANT));
9257 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9258 SVN_ERR(svn_sqlite__step(conflict_exists, stmt));
9260 return svn_error_trace(svn_sqlite__reset(stmt));
9263 /* What we really want to store about a node. This relies on the
9264 offset of svn_wc__db_info_t being zero. */
9265 struct read_children_info_item_t
9267 struct svn_wc__db_info_t info;
9270 svn_boolean_t was_dir;
9273 /* Implementation of svn_wc__db_read_children_info */
9274 static svn_error_t *
9275 read_children_info(svn_wc__db_wcroot_t *wcroot,
9276 const char *dir_relpath,
9277 apr_hash_t *conflicts,
9279 svn_boolean_t base_tree_only,
9280 apr_pool_t *result_pool,
9281 apr_pool_t *scratch_pool)
9283 svn_sqlite__stmt_t *stmt;
9284 svn_boolean_t have_row;
9285 const char *repos_root_url = NULL;
9286 const char *repos_uuid = NULL;
9287 apr_int64_t last_repos_id = INVALID_REPOS_ID;
9288 const char *last_repos_root_url = NULL;
9290 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9292 ? STMT_SELECT_BASE_NODE_CHILDREN_INFO
9293 : STMT_SELECT_NODE_CHILDREN_INFO)));
9294 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9295 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9299 /* CHILD item points to what we have about the node. We only provide
9300 CHILD->item to our caller. */
9301 struct read_children_info_item_t *child_item;
9302 const char *child_relpath = svn_sqlite__column_text(stmt, 19, NULL);
9303 const char *name = svn_relpath_basename(child_relpath, NULL);
9306 svn_boolean_t new_child;
9308 child_item = (base_tree_only ? NULL : svn_hash_gets(nodes, name));
9313 child_item = apr_pcalloc(result_pool, sizeof(*child_item));
9317 op_depth = svn_sqlite__column_int(stmt, 0);
9319 /* Do we have new or better information? */
9322 struct svn_wc__db_info_t *child = &child_item->info;
9323 child_item->op_depth = op_depth;
9325 child->kind = svn_sqlite__column_token(stmt, 4, kind_map);
9327 child->status = svn_sqlite__column_token(stmt, 3, presence_map);
9330 if (child->status == svn_wc__db_status_incomplete)
9331 child->incomplete = TRUE;
9332 err = convert_to_working_status(&child->status, child->status);
9334 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9338 child->revnum = SVN_INVALID_REVNUM;
9340 child->revnum = svn_sqlite__column_revnum(stmt, 5);
9343 child->repos_relpath = NULL;
9345 child->repos_relpath = svn_sqlite__column_text(stmt, 2,
9348 if (op_depth != 0 || svn_sqlite__column_is_null(stmt, 1))
9350 child->repos_root_url = NULL;
9351 child->repos_uuid = NULL;
9355 apr_int64_t repos_id = svn_sqlite__column_int64(stmt, 1);
9356 if (!repos_root_url ||
9357 (last_repos_id != INVALID_REPOS_ID &&
9358 repos_id != last_repos_id))
9360 last_repos_root_url = repos_root_url;
9361 err = svn_wc__db_fetch_repos_info(&repos_root_url,
9366 SVN_ERR(svn_error_compose_create(err,
9367 svn_sqlite__reset(stmt)));
9370 if (last_repos_id == INVALID_REPOS_ID)
9371 last_repos_id = repos_id;
9373 /* Assume working copy is all one repos_id so that a
9374 single cached value is sufficient. */
9375 if (repos_id != last_repos_id)
9377 err= svn_error_createf(
9378 SVN_ERR_WC_DB_ERROR, NULL,
9379 _("The node '%s' comes from unexpected repository "
9380 "'%s', expected '%s'; if this node is a file "
9381 "external using the correct URL in the external "
9382 "definition can fix the problem, see issue #4087"),
9383 child_relpath, repos_root_url, last_repos_root_url);
9384 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
9386 child->repos_root_url = repos_root_url;
9387 child->repos_uuid = repos_uuid;
9390 child->changed_rev = svn_sqlite__column_revnum(stmt, 8);
9392 child->changed_date = svn_sqlite__column_int64(stmt, 9);
9394 child->changed_author = svn_sqlite__column_text(stmt, 10,
9397 if (child->kind != svn_node_dir)
9398 child->depth = svn_depth_unknown;
9401 child->has_descendants = TRUE;
9402 child_item->was_dir = TRUE;
9403 child->depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
9407 err = is_wclocked(&child->locked, wcroot, child_relpath,
9411 SVN_ERR(svn_error_compose_create(err,
9412 svn_sqlite__reset(stmt)));
9416 child->recorded_time = svn_sqlite__column_int64(stmt, 13);
9417 child->recorded_size = get_recorded_size(stmt, 7);
9418 child->has_checksum = !svn_sqlite__column_is_null(stmt, 6);
9419 child->copied = op_depth > 0 && !svn_sqlite__column_is_null(stmt, 2);
9420 child->had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
9422 if (child->had_props)
9424 apr_hash_t *properties;
9425 err = svn_sqlite__column_properties(&properties, stmt, 14,
9426 scratch_pool, scratch_pool);
9428 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9430 child->special = (child->had_props
9431 && svn_hash_gets(properties, SVN_PROP_SPECIAL));
9435 child->op_root = FALSE;
9437 child->op_root = (op_depth == relpath_depth(child_relpath));
9439 if (op_depth && child->op_root)
9440 child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
9443 svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
9445 else if (!child_item->was_dir
9446 && svn_sqlite__column_token(stmt, 4, kind_map) == svn_node_dir)
9448 child_item->was_dir = TRUE;
9450 err = find_conflict_descendants(&child_item->info.has_descendants,
9451 wcroot, child_relpath,
9454 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9459 child_item->info.have_base = TRUE;
9461 /* Get the lock info, available only at op_depth 0. */
9462 child_item->info.lock = lock_from_columns(stmt, 15, 16, 17, 18,
9465 /* FILE_EXTERNAL flag only on op_depth 0. */
9466 child_item->info.file_external = svn_sqlite__column_boolean(stmt,
9471 const char *moved_to_relpath;
9473 child_item->nr_layers++;
9474 child_item->info.have_more_work = (child_item->nr_layers > 1);
9477 /* A local_relpath can be moved multiple times at different op
9478 depths and it really depends on the caller what is interesting.
9479 We provide a simple linked list with the moved_from information */
9481 moved_to_relpath = svn_sqlite__column_text(stmt, 21, NULL);
9482 if (moved_to_relpath)
9484 struct svn_wc__db_moved_to_info_t *moved_to;
9485 struct svn_wc__db_moved_to_info_t **next;
9486 const char *shadow_op_relpath;
9488 moved_to = apr_pcalloc(result_pool, sizeof(*moved_to));
9489 moved_to->moved_to_abspath = svn_dirent_join(wcroot->abspath,
9493 shadow_op_relpath = svn_relpath_prefix(child_relpath, op_depth,
9496 moved_to->shadow_op_root_abspath =
9497 svn_dirent_join(wcroot->abspath, shadow_op_relpath,
9500 next = &child_item->info.moved_to;
9503 0 < strcmp((*next)->shadow_op_root_abspath,
9504 moved_to->shadow_op_root_abspath))
9505 next = &((*next)->next);
9507 moved_to->next = *next;
9512 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9515 SVN_ERR(svn_sqlite__reset(stmt));
9517 if (!base_tree_only)
9519 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9520 STMT_SELECT_ACTUAL_CHILDREN_INFO));
9521 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9522 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9526 struct read_children_info_item_t *child_item;
9527 struct svn_wc__db_info_t *child;
9528 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9529 const char *name = svn_relpath_basename(child_relpath, NULL);
9531 child_item = svn_hash_gets(nodes, name);
9534 child_item = apr_pcalloc(result_pool, sizeof(*child_item));
9535 child_item->info.status = svn_wc__db_status_not_present;
9538 child = &child_item->info;
9540 child->changelist = svn_sqlite__column_text(stmt, 1, result_pool);
9542 child->props_mod = !svn_sqlite__column_is_null(stmt, 2);
9544 if (child->props_mod)
9547 apr_hash_t *properties;
9549 err = svn_sqlite__column_properties(&properties, stmt, 2,
9550 scratch_pool, scratch_pool);
9552 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9553 child->special = (NULL != svn_hash_gets(properties,
9559 child->conflicted = !svn_sqlite__column_is_null(stmt, 3);
9561 if (child->conflicted)
9562 svn_hash_sets(conflicts, apr_pstrdup(result_pool, name), "");
9564 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9567 SVN_ERR(svn_sqlite__reset(stmt));
9570 return SVN_NO_ERROR;
9574 svn_wc__db_read_children_info(apr_hash_t **nodes,
9575 apr_hash_t **conflicts,
9577 const char *dir_abspath,
9578 svn_boolean_t base_tree_only,
9579 apr_pool_t *result_pool,
9580 apr_pool_t *scratch_pool)
9582 svn_wc__db_wcroot_t *wcroot;
9583 const char *dir_relpath;
9585 *conflicts = apr_hash_make(result_pool);
9586 *nodes = apr_hash_make(result_pool);
9587 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9589 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9591 scratch_pool, scratch_pool));
9592 VERIFY_USABLE_WCROOT(wcroot);
9594 SVN_WC__DB_WITH_TXN(
9595 read_children_info(wcroot, dir_relpath, *conflicts, *nodes,
9596 base_tree_only, result_pool, scratch_pool),
9599 return SVN_NO_ERROR;
9602 /* Implementation of svn_wc__db_read_single_info.
9604 ### This function is very similar to a lot of code inside
9605 read_children_info, but that performs some tricks to re-use data between
9608 ### We read the same few NODES records a few times via different helper
9609 functions, so this could be optimized bit, but everything is within
9610 a sqlite transaction and all queries are backed by an index, so generally
9611 everything (including the used indexes) should be in the sqlite page cache
9612 after the first query.
9614 static svn_error_t *
9615 read_single_info(const struct svn_wc__db_info_t **info,
9616 svn_wc__db_wcroot_t *wcroot,
9617 const char *local_relpath,
9618 svn_boolean_t base_tree_only,
9619 apr_pool_t *result_pool,
9620 apr_pool_t *scratch_pool)
9622 struct svn_wc__db_info_t *mtb;
9623 apr_int64_t repos_id;
9624 const svn_checksum_t *checksum;
9625 const char *original_repos_relpath;
9626 svn_boolean_t have_work;
9627 apr_hash_t *properties;
9629 mtb = apr_pcalloc(result_pool, sizeof(*mtb));
9631 if (!base_tree_only)
9632 SVN_ERR(read_info(&mtb->status, &mtb->kind, &mtb->revnum,
9633 &mtb->repos_relpath, &repos_id, &mtb->changed_rev,
9634 &mtb->changed_date, &mtb->changed_author, &mtb->depth,
9635 &checksum, NULL, &original_repos_relpath, NULL, NULL,
9636 &mtb->lock, &mtb->recorded_size, &mtb->recorded_time,
9637 &mtb->changelist, &mtb->conflicted, &mtb->op_root,
9638 &mtb->had_props, &mtb->props_mod, &mtb->have_base,
9639 &mtb->have_more_work, &have_work,
9640 wcroot, local_relpath, result_pool, scratch_pool));
9643 svn_boolean_t update_root;
9646 original_repos_relpath = NULL;
9648 SVN_ERR(svn_wc__db_base_get_info_internal(
9649 &mtb->status, &mtb->kind, &mtb->revnum, &mtb->repos_relpath,
9650 &repos_id, &mtb->changed_rev, &mtb->changed_date,
9651 &mtb->changed_author, &mtb->depth, &checksum, NULL,
9652 &mtb->lock, &mtb->had_props, &properties, &update_root,
9653 wcroot, local_relpath, scratch_pool, scratch_pool));
9655 mtb->have_base = TRUE;
9656 mtb->file_external = (update_root && mtb->kind == svn_node_file);
9659 /* Query the same rows in the database again for move information */
9660 if (have_work && (mtb->have_base || mtb->have_more_work))
9662 svn_sqlite__stmt_t *stmt;
9663 svn_boolean_t have_row;
9665 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9666 STMT_SELECT_MOVED_TO_NODE));
9667 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9669 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9673 struct svn_wc__db_moved_to_info_t *move;
9674 int op_depth = svn_sqlite__column_int(stmt, 0);
9675 const char *moved_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
9676 const char *cur_relpath;
9678 move = apr_pcalloc(result_pool, sizeof(*move));
9679 move->moved_to_abspath = svn_dirent_join(wcroot->abspath,
9683 cur_relpath = svn_relpath_prefix(local_relpath, op_depth,
9686 move->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath,
9690 move->next = mtb->moved_to;
9691 mtb->moved_to = move;
9693 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9696 SVN_ERR(svn_sqlite__reset(stmt));
9699 /* Maybe we have to get some shadowed lock from BASE to make our test suite
9700 happy... (It might be completely unrelated, but...)
9701 This queries the same BASE row again, joined to the lock table */
9702 if (!base_tree_only && mtb->have_base
9703 && (have_work || mtb->kind == svn_node_file))
9705 svn_boolean_t update_root;
9706 svn_wc__db_lock_t **lock_arg = NULL;
9709 lock_arg = &mtb->lock;
9711 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL, NULL,
9712 NULL, NULL, NULL, NULL, NULL,
9713 NULL, lock_arg, NULL, NULL,
9715 wcroot, local_relpath,
9716 result_pool, scratch_pool));
9718 mtb->file_external = (update_root && mtb->kind == svn_node_file);
9721 if (mtb->status == svn_wc__db_status_added)
9723 svn_wc__db_status_t status;
9725 SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9727 wcroot, local_relpath,
9728 result_pool, scratch_pool));
9730 mtb->moved_here = (status == svn_wc__db_status_moved_here);
9731 mtb->incomplete = (status == svn_wc__db_status_incomplete);
9735 if (mtb->kind == svn_node_file
9736 && (mtb->had_props || mtb->props_mod
9737 || (base_tree_only && properties)))
9739 if (!base_tree_only)
9742 SVN_ERR(svn_wc__db_read_props_internal(&properties,
9743 wcroot, local_relpath,
9744 scratch_pool, scratch_pool));
9746 SVN_ERR(db_read_pristine_props(&properties, wcroot, local_relpath,
9747 TRUE /* deleted_ok */,
9748 scratch_pool, scratch_pool));
9751 mtb->special = (NULL != svn_hash_gets(properties, SVN_PROP_SPECIAL));
9755 mtb->has_checksum = (checksum != NULL);
9756 mtb->copied = (original_repos_relpath != NULL);
9758 SVN_ERR(svn_wc__db_fetch_repos_info(&mtb->repos_root_url, &mtb->repos_uuid,
9759 wcroot, repos_id, result_pool));
9761 if (!base_tree_only && mtb->kind == svn_node_dir)
9762 SVN_ERR(is_wclocked(&mtb->locked, wcroot, local_relpath, scratch_pool));
9764 if (mtb->kind == svn_node_dir)
9765 mtb->has_descendants = TRUE;
9767 SVN_ERR(find_conflict_descendants(&mtb->has_descendants,
9768 wcroot, local_relpath, scratch_pool));
9772 return SVN_NO_ERROR;
9776 svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info,
9778 const char *local_abspath,
9779 svn_boolean_t base_tree_only,
9780 apr_pool_t *result_pool,
9781 apr_pool_t *scratch_pool)
9783 svn_wc__db_wcroot_t *wcroot;
9784 const char *local_relpath;
9786 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9788 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9790 scratch_pool, scratch_pool));
9791 VERIFY_USABLE_WCROOT(wcroot);
9793 SVN_WC__DB_WITH_TXN(read_single_info(info, wcroot, local_relpath,
9795 result_pool, scratch_pool),
9798 return SVN_NO_ERROR;
9802 svn_wc__db_read_pristine_info(svn_wc__db_status_t *status,
9803 svn_node_kind_t *kind,
9804 svn_revnum_t *changed_rev,
9805 apr_time_t *changed_date,
9806 const char **changed_author,
9807 svn_depth_t *depth, /* dirs only */
9808 const svn_checksum_t **checksum, /* files only */
9809 const char **target, /* symlinks only */
9810 svn_boolean_t *had_props,
9813 const char *local_abspath,
9814 apr_pool_t *result_pool,
9815 apr_pool_t *scratch_pool)
9817 svn_wc__db_wcroot_t *wcroot;
9818 const char *local_relpath;
9819 svn_sqlite__stmt_t *stmt;
9820 svn_boolean_t have_row;
9821 svn_error_t *err = NULL;
9823 svn_wc__db_status_t raw_status;
9824 svn_node_kind_t node_kind;
9826 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9828 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9830 scratch_pool, scratch_pool));
9831 VERIFY_USABLE_WCROOT(wcroot);
9833 /* Obtain the most likely to exist record first, to make sure we don't
9834 have to obtain the SQLite read-lock multiple times */
9835 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9836 STMT_SELECT_NODE_INFO));
9837 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9838 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9842 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9843 svn_sqlite__reset(stmt),
9844 _("The node '%s' was not found."),
9845 path_for_error_message(wcroot,
9850 op_depth = svn_sqlite__column_int(stmt, 0);
9851 raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9853 if (op_depth > 0 && raw_status == svn_wc__db_status_base_deleted)
9855 SVN_ERR(svn_sqlite__step_row(stmt));
9857 op_depth = svn_sqlite__column_int(stmt, 0);
9858 raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9861 node_kind = svn_sqlite__column_token(stmt, 4, kind_map);
9867 err = svn_error_compose_create(err,
9868 convert_to_working_status(
9873 *status = raw_status;
9881 *changed_rev = svn_sqlite__column_revnum(stmt, 8);
9885 *changed_date = svn_sqlite__column_int64(stmt, 9);
9889 *changed_author = svn_sqlite__column_text(stmt, 10,
9894 if (node_kind != svn_node_dir)
9896 *depth = svn_depth_unknown;
9900 *depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
9906 if (node_kind != svn_node_file)
9913 err2 = svn_sqlite__column_checksum(checksum, stmt, 6, result_pool);
9918 err = svn_error_compose_create(
9922 _("The node '%s' has a corrupt checksum value."),
9923 path_for_error_message(wcroot, local_relpath,
9932 if (node_kind != svn_node_symlink)
9935 *target = svn_sqlite__column_text(stmt, 12, result_pool);
9939 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
9943 if (raw_status == svn_wc__db_status_normal
9944 || raw_status == svn_wc__db_status_incomplete)
9946 SVN_ERR(svn_sqlite__column_properties(props, stmt, 14,
9947 result_pool, scratch_pool));
9949 *props = apr_hash_make(result_pool);
9953 assert(svn_sqlite__column_is_null(stmt, 14));
9958 return svn_error_trace(
9959 svn_error_compose_create(err,
9960 svn_sqlite__reset(stmt)));
9964 svn_wc__db_read_children_walker_info(const apr_array_header_t **items,
9966 const char *dir_abspath,
9967 apr_pool_t *result_pool,
9968 apr_pool_t *scratch_pool)
9970 svn_wc__db_wcroot_t *wcroot;
9971 const char *dir_relpath;
9972 svn_sqlite__stmt_t *stmt;
9973 svn_boolean_t have_row;
9974 apr_array_header_t *nodes;
9976 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9978 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9980 scratch_pool, scratch_pool));
9981 VERIFY_USABLE_WCROOT(wcroot);
9983 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9984 STMT_SELECT_NODE_CHILDREN_WALKER_INFO));
9985 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9986 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9988 nodes = apr_array_make(result_pool, 16,
9989 sizeof(struct svn_wc__db_walker_info_t *));
9992 struct svn_wc__db_walker_info_t *child;
9993 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9994 const char *name = svn_relpath_basename(child_relpath, result_pool);
9995 int op_depth = svn_sqlite__column_int(stmt, 1);
9998 child = apr_palloc(result_pool, sizeof(*child));
10000 child->status = svn_sqlite__column_token(stmt, 2, presence_map);
10003 err = convert_to_working_status(&child->status, child->status);
10005 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10007 child->kind = svn_sqlite__column_token(stmt, 3, kind_map);
10009 APR_ARRAY_PUSH(nodes, struct svn_wc__db_walker_info_t *) = child;
10011 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10014 SVN_ERR(svn_sqlite__reset(stmt));
10018 return SVN_NO_ERROR;
10022 svn_wc__db_read_node_install_info(const char **wcroot_abspath,
10023 const svn_checksum_t **sha1_checksum,
10024 apr_hash_t **pristine_props,
10025 apr_time_t *changed_date,
10027 const char *local_abspath,
10028 const char *wri_abspath,
10029 apr_pool_t *result_pool,
10030 apr_pool_t *scratch_pool)
10032 svn_wc__db_wcroot_t *wcroot;
10033 const char *local_relpath;
10034 svn_sqlite__stmt_t *stmt;
10035 svn_error_t *err = NULL;
10036 svn_boolean_t have_row;
10038 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10041 wri_abspath = local_abspath;
10043 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10044 wri_abspath, scratch_pool, scratch_pool));
10045 VERIFY_USABLE_WCROOT(wcroot);
10047 if (local_abspath != wri_abspath
10048 && strcmp(local_abspath, wri_abspath))
10050 if (!svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
10051 return svn_error_createf(
10052 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
10053 _("The node '%s' is not in working copy '%s'"),
10054 svn_dirent_local_style(local_abspath, scratch_pool),
10055 svn_dirent_local_style(wcroot->abspath, scratch_pool));
10057 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
10060 if (wcroot_abspath != NULL)
10061 *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
10063 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10064 STMT_SELECT_NODE_INFO));
10066 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10068 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10073 err = svn_sqlite__column_checksum(sha1_checksum, stmt, 6, result_pool);
10075 if (!err && pristine_props)
10077 err = svn_sqlite__column_properties(pristine_props, stmt, 14,
10078 result_pool, scratch_pool);
10079 /* Null means no props (assuming presence normal or incomplete). */
10080 if (*pristine_props == NULL)
10081 *pristine_props = apr_hash_make(result_pool);
10085 *changed_date = svn_sqlite__column_int64(stmt, 9);
10088 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10089 svn_sqlite__reset(stmt),
10090 _("The node '%s' is not installable"),
10091 svn_dirent_local_style(local_abspath,
10094 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10096 return SVN_NO_ERROR;
10101 /* The body of svn_wc__db_read_repos_info().
10103 static svn_error_t *
10104 db_read_repos_info(svn_revnum_t *revision,
10105 const char **repos_relpath,
10106 apr_int64_t *repos_id,
10107 svn_wc__db_wcroot_t *wcroot,
10108 const char *local_relpath,
10109 apr_pool_t *result_pool,
10110 apr_pool_t *scratch_pool)
10112 svn_wc__db_status_t status;
10114 SVN_ERR(read_info(&status, NULL, revision, repos_relpath, repos_id, NULL,
10115 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10116 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10118 wcroot, local_relpath, result_pool, scratch_pool));
10120 if ((repos_relpath && !*repos_relpath)
10121 || (repos_id && *repos_id == INVALID_REPOS_ID))
10123 if (status == svn_wc__db_status_added)
10125 SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL,
10126 NULL, NULL, NULL, NULL, NULL,
10127 wcroot, local_relpath,
10128 result_pool, scratch_pool));
10130 else if (status == svn_wc__db_status_deleted)
10132 const char *base_del_relpath;
10133 const char *work_del_relpath;
10135 SVN_ERR(scan_deletion(&base_del_relpath, NULL,
10142 if (work_del_relpath)
10144 /* The parent of the WORKING delete, must be an addition */
10145 const char *work_relpath = NULL;
10147 /* work_del_relpath should not be NULL. However, we have
10148 * observed instances where that assumption was not met.
10149 * Bail out in that case instead of crashing with a segfault.
10151 SVN_ERR_ASSERT(work_del_relpath != NULL);
10152 work_relpath = svn_relpath_dirname(work_del_relpath,
10155 SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id,
10156 NULL, NULL, NULL, NULL, NULL, NULL,
10157 wcroot, work_relpath,
10158 scratch_pool, scratch_pool));
10161 *repos_relpath = svn_relpath_join(
10163 svn_dirent_skip_ancestor(work_relpath,
10169 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, revision,
10174 NULL, NULL, NULL, NULL,
10181 *repos_relpath = svn_relpath_join(
10183 svn_dirent_skip_ancestor(base_del_relpath,
10188 else if (status == svn_wc__db_status_excluded)
10190 const char *parent_relpath;
10193 /* A BASE excluded would have had repository information, so
10194 we have a working exclude, which must be below an addition */
10196 svn_relpath_split(&parent_relpath, &name, local_relpath,
10198 SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL,
10199 NULL, NULL, NULL, NULL, NULL,
10200 wcroot, parent_relpath,
10201 scratch_pool, scratch_pool));
10204 *repos_relpath = svn_relpath_join(*repos_relpath, name,
10207 return SVN_NO_ERROR;
10211 /* All working statee are explicitly handled and all base statee
10212 have a repos_relpath */
10213 SVN_ERR_MALFUNCTION();
10217 return SVN_NO_ERROR;
10222 svn_wc__db_read_repos_info(svn_revnum_t *revision,
10223 const char **repos_relpath,
10224 const char **repos_root_url,
10225 const char **repos_uuid,
10227 const char *local_abspath,
10228 apr_pool_t *result_pool,
10229 apr_pool_t *scratch_pool)
10231 svn_wc__db_wcroot_t *wcroot;
10232 const char *local_relpath;
10233 apr_int64_t repos_id = INVALID_REPOS_ID;
10235 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10237 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10239 scratch_pool, scratch_pool));
10240 VERIFY_USABLE_WCROOT(wcroot);
10242 SVN_WC__DB_WITH_TXN4(db_read_repos_info(revision, repos_relpath,
10243 (repos_root_url || repos_uuid)
10244 ? &repos_id : NULL,
10245 wcroot, local_relpath,
10246 result_pool, scratch_pool),
10247 svn_wc__db_fetch_repos_info(repos_root_url,
10251 SVN_NO_ERROR, SVN_NO_ERROR,
10254 return SVN_NO_ERROR;
10258 /* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
10259 a hash table mapping <tt>char *</tt> names onto svn_string_t *
10260 values for any properties of immediate or recursive child nodes of
10261 LOCAL_ABSPATH, the actual query being determined by STMT_IDX.
10262 If FILES_ONLY is true, only report properties for file child nodes.
10263 Check for cancellation between calls of RECEIVER_FUNC.
10265 typedef struct cache_props_baton_t
10268 svn_boolean_t pristine;
10269 const apr_array_header_t *changelists;
10270 svn_cancel_func_t cancel_func;
10271 void *cancel_baton;
10272 } cache_props_baton_t;
10275 static svn_error_t *
10276 cache_props_recursive(void *cb_baton,
10277 svn_wc__db_wcroot_t *wcroot,
10278 const char *local_relpath,
10279 apr_pool_t *scratch_pool)
10281 cache_props_baton_t *baton = cb_baton;
10282 svn_sqlite__stmt_t *stmt;
10285 SVN_ERR(populate_targets_tree(wcroot, local_relpath, baton->depth,
10286 baton->changelists, scratch_pool));
10288 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
10289 STMT_CREATE_TARGET_PROP_CACHE));
10291 if (baton->pristine)
10292 stmt_idx = STMT_CACHE_TARGET_PRISTINE_PROPS;
10294 stmt_idx = STMT_CACHE_TARGET_PROPS;
10296 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
10297 SVN_ERR(svn_sqlite__bind_int64(stmt, 1, wcroot->wc_id));
10298 SVN_ERR(svn_sqlite__step_done(stmt));
10300 return SVN_NO_ERROR;
10305 svn_wc__db_read_props_streamily(svn_wc__db_t *db,
10306 const char *local_abspath,
10308 svn_boolean_t pristine,
10309 const apr_array_header_t *changelists,
10310 svn_wc__proplist_receiver_t receiver_func,
10311 void *receiver_baton,
10312 svn_cancel_func_t cancel_func,
10313 void *cancel_baton,
10314 apr_pool_t *scratch_pool)
10316 svn_wc__db_wcroot_t *wcroot;
10317 const char *local_relpath;
10318 svn_sqlite__stmt_t *stmt;
10319 cache_props_baton_t baton;
10320 svn_boolean_t have_row;
10321 apr_pool_t *iterpool;
10322 svn_error_t *err = NULL;
10324 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10325 SVN_ERR_ASSERT(receiver_func);
10326 SVN_ERR_ASSERT((depth == svn_depth_files) ||
10327 (depth == svn_depth_immediates) ||
10328 (depth == svn_depth_infinity));
10330 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10332 scratch_pool, scratch_pool));
10333 VERIFY_USABLE_WCROOT(wcroot);
10335 baton.depth = depth;
10336 baton.pristine = pristine;
10337 baton.changelists = changelists;
10338 baton.cancel_func = cancel_func;
10339 baton.cancel_baton = cancel_baton;
10341 SVN_ERR(with_finalization(wcroot, local_relpath,
10342 cache_props_recursive, &baton,
10344 cancel_func, cancel_baton,
10346 STMT_DROP_TARGETS_LIST,
10349 iterpool = svn_pool_create(scratch_pool);
10351 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10352 STMT_SELECT_ALL_TARGET_PROP_CACHE));
10353 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10354 while (!err && have_row)
10358 svn_pool_clear(iterpool);
10360 SVN_ERR(svn_sqlite__column_properties(&props, stmt, 1, iterpool,
10363 /* see if someone wants to cancel this operation. */
10365 err = cancel_func(cancel_baton);
10367 if (!err && props && apr_hash_count(props) != 0)
10369 const char *child_relpath;
10370 const char *child_abspath;
10372 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
10373 child_abspath = svn_dirent_join(wcroot->abspath,
10374 child_relpath, iterpool);
10376 err = receiver_func(receiver_baton, child_abspath, props, iterpool);
10379 err = svn_error_compose_create(err, svn_sqlite__step(&have_row, stmt));
10382 err = svn_error_compose_create(err, svn_sqlite__reset(stmt));
10384 svn_pool_destroy(iterpool);
10386 SVN_ERR(svn_error_compose_create(
10388 svn_sqlite__exec_statements(wcroot->sdb,
10389 STMT_DROP_TARGET_PROP_CACHE)));
10390 return SVN_NO_ERROR;
10394 /* Helper for svn_wc__db_read_props().
10397 svn_wc__db_read_props_internal(apr_hash_t **props,
10398 svn_wc__db_wcroot_t *wcroot,
10399 const char *local_relpath,
10400 apr_pool_t *result_pool,
10401 apr_pool_t *scratch_pool)
10403 svn_sqlite__stmt_t *stmt;
10404 svn_boolean_t have_row;
10405 svn_error_t *err = NULL;
10407 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10408 STMT_SELECT_ACTUAL_PROPS));
10409 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10410 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10412 if (have_row && !svn_sqlite__column_is_null(stmt, 0))
10414 err = svn_sqlite__column_properties(props, stmt, 0,
10415 result_pool, scratch_pool);
10420 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10423 return SVN_NO_ERROR;
10425 /* No local changes. Return the pristine props for this node. */
10426 SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, FALSE,
10427 result_pool, scratch_pool));
10428 if (*props == NULL)
10430 /* Pristine properties are not defined for this node.
10431 ### we need to determine whether this node is in a state that
10432 ### allows for ACTUAL properties (ie. not deleted). for now,
10433 ### just say all nodes, no matter the state, have at least an
10434 ### empty set of props. */
10435 *props = apr_hash_make(result_pool);
10438 return SVN_NO_ERROR;
10443 svn_wc__db_read_props(apr_hash_t **props,
10445 const char *local_abspath,
10446 apr_pool_t *result_pool,
10447 apr_pool_t *scratch_pool)
10449 svn_wc__db_wcroot_t *wcroot;
10450 const char *local_relpath;
10452 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10454 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10455 local_abspath, scratch_pool, scratch_pool));
10456 VERIFY_USABLE_WCROOT(wcroot);
10458 SVN_WC__DB_WITH_TXN(svn_wc__db_read_props_internal(props, wcroot,
10464 return SVN_NO_ERROR;
10468 static svn_error_t *
10469 db_read_pristine_props(apr_hash_t **props,
10470 svn_wc__db_wcroot_t *wcroot,
10471 const char *local_relpath,
10472 svn_boolean_t deleted_ok,
10473 apr_pool_t *result_pool,
10474 apr_pool_t *scratch_pool)
10476 svn_sqlite__stmt_t *stmt;
10477 svn_boolean_t have_row;
10478 svn_wc__db_status_t presence;
10480 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_NODE_PROPS));
10481 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10483 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10487 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10488 svn_sqlite__reset(stmt),
10489 _("The node '%s' was not found."),
10490 path_for_error_message(wcroot,
10496 /* Examine the presence: */
10497 presence = svn_sqlite__column_token(stmt, 1, presence_map);
10499 /* For "base-deleted", it is obvious the pristine props are located
10500 below the current node. Fetch the NODE from the next record. */
10501 if (presence == svn_wc__db_status_base_deleted && deleted_ok)
10503 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10505 SVN_ERR_ASSERT(have_row);
10507 presence = svn_sqlite__column_token(stmt, 1, presence_map);
10510 /* normal or copied: Fetch properties (during update we want
10511 properties for incomplete as well) */
10512 if (presence == svn_wc__db_status_normal
10513 || presence == svn_wc__db_status_incomplete)
10517 err = svn_sqlite__column_properties(props, stmt, 0, result_pool,
10519 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10522 *props = apr_hash_make(result_pool);
10524 return SVN_NO_ERROR;
10527 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
10528 svn_sqlite__reset(stmt),
10529 _("The node '%s' has a status that"
10530 " has no properties."),
10531 path_for_error_message(wcroot,
10538 svn_wc__db_read_pristine_props(apr_hash_t **props,
10540 const char *local_abspath,
10541 apr_pool_t *result_pool,
10542 apr_pool_t *scratch_pool)
10544 svn_wc__db_wcroot_t *wcroot;
10545 const char *local_relpath;
10547 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10549 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10550 local_abspath, scratch_pool, scratch_pool));
10551 VERIFY_USABLE_WCROOT(wcroot);
10553 SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, TRUE,
10554 result_pool, scratch_pool));
10555 return SVN_NO_ERROR;
10559 svn_wc__db_prop_retrieve_recursive(apr_hash_t **values,
10561 const char *local_abspath,
10562 const char *propname,
10563 apr_pool_t *result_pool,
10564 apr_pool_t *scratch_pool)
10566 svn_wc__db_wcroot_t *wcroot;
10567 const char *local_relpath;
10568 svn_sqlite__stmt_t *stmt;
10569 svn_boolean_t have_row;
10570 apr_pool_t *iterpool;
10572 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10574 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10575 local_abspath, scratch_pool, scratch_pool));
10576 VERIFY_USABLE_WCROOT(wcroot);
10578 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10579 STMT_SELECT_CURRENT_PROPS_RECURSIVE));
10580 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10582 *values = apr_hash_make(result_pool);
10584 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10585 iterpool = svn_pool_create(scratch_pool);
10588 apr_hash_t *node_props;
10589 svn_string_t *value;
10591 svn_pool_clear(iterpool);
10593 SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 0,
10594 iterpool, iterpool));
10596 value = (node_props
10597 ? svn_hash_gets(node_props, propname)
10602 svn_hash_sets(*values,
10603 svn_dirent_join(wcroot->abspath,
10604 svn_sqlite__column_text(stmt, 1, NULL),
10606 svn_string_dup(value, result_pool));
10609 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10612 svn_pool_destroy(iterpool);
10614 return svn_error_trace(svn_sqlite__reset(stmt));
10617 /* The body of svn_wc__db_read_cached_iprops(). */
10618 static svn_error_t *
10619 db_read_cached_iprops(apr_array_header_t **iprops,
10620 svn_wc__db_wcroot_t *wcroot,
10621 const char *local_relpath,
10622 apr_pool_t *result_pool,
10623 apr_pool_t *scratch_pool)
10625 svn_sqlite__stmt_t *stmt;
10626 svn_boolean_t have_row;
10628 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_IPROPS));
10629 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10630 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10634 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10635 svn_sqlite__reset(stmt),
10636 _("The node '%s' was not found."),
10637 path_for_error_message(wcroot, local_relpath,
10641 SVN_ERR(svn_sqlite__column_iprops(iprops, stmt, 0,
10642 result_pool, scratch_pool));
10644 SVN_ERR(svn_sqlite__reset(stmt));
10646 return SVN_NO_ERROR;
10650 svn_wc__db_read_cached_iprops(apr_array_header_t **iprops,
10652 const char *local_abspath,
10653 apr_pool_t *result_pool,
10654 apr_pool_t *scratch_pool)
10656 svn_wc__db_wcroot_t *wcroot;
10657 const char *local_relpath;
10659 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10661 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10663 scratch_pool, scratch_pool));
10664 VERIFY_USABLE_WCROOT(wcroot);
10666 /* Don't use with_txn yet, as we perform just a single transaction */
10667 SVN_ERR(db_read_cached_iprops(iprops, wcroot, local_relpath,
10668 result_pool, scratch_pool));
10672 *iprops = apr_array_make(result_pool, 0,
10673 sizeof(svn_prop_inherited_item_t *));
10676 return SVN_NO_ERROR;
10679 /* Remove all prop name value pairs from PROP_HASH where the property
10680 name is not PROPNAME. */
10682 filter_unwanted_props(apr_hash_t *prop_hash,
10683 const char * propname,
10684 apr_pool_t *scratch_pool)
10686 apr_hash_index_t *hi;
10688 for (hi = apr_hash_first(scratch_pool, prop_hash);
10690 hi = apr_hash_next(hi))
10692 const char *ipropname = apr_hash_this_key(hi);
10694 if (strcmp(ipropname, propname) != 0)
10695 svn_hash_sets(prop_hash, ipropname, NULL);
10700 /* Get the changed properties as stored in the ACTUAL table */
10701 static svn_error_t *
10702 db_get_changed_props(apr_hash_t **actual_props,
10703 svn_wc__db_wcroot_t *wcroot,
10704 const char *local_relpath,
10705 apr_pool_t *result_pool,
10706 apr_pool_t *scratch_pool)
10708 svn_sqlite__stmt_t *stmt;
10709 svn_boolean_t have_row;
10710 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10711 STMT_SELECT_ACTUAL_PROPS));
10712 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10713 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10715 if (have_row && !svn_sqlite__column_is_null(stmt, 0))
10716 SVN_ERR(svn_sqlite__column_properties(actual_props, stmt, 0,
10717 result_pool, scratch_pool));
10719 *actual_props = NULL; /* Cached when we read that record */
10721 return svn_error_trace(svn_sqlite__reset(stmt));
10724 /* The body of svn_wc__db_read_inherited_props(). */
10725 static svn_error_t *
10726 db_read_inherited_props(apr_array_header_t **inherited_props,
10727 apr_hash_t **actual_props,
10728 svn_wc__db_wcroot_t *wcroot,
10729 const char *local_relpath,
10730 const char *propname,
10731 apr_pool_t *result_pool,
10732 apr_pool_t *scratch_pool)
10735 apr_array_header_t *cached_iprops = NULL;
10736 apr_array_header_t *iprops;
10737 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
10738 svn_sqlite__stmt_t *stmt;
10739 const char *relpath;
10740 const char *expected_parent_repos_relpath = NULL;
10741 const char *parent_relpath;
10743 iprops = apr_array_make(result_pool, 1,
10744 sizeof(svn_prop_inherited_item_t *));
10745 *inherited_props = iprops;
10748 *actual_props = NULL;
10750 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10751 STMT_SELECT_NODE_INFO));
10753 relpath = local_relpath;
10755 /* Walk up to the root of the WC looking for inherited properties. When we
10756 reach the WC root also check for cached inherited properties. */
10757 for (relpath = local_relpath; relpath; relpath = parent_relpath)
10759 svn_boolean_t have_row;
10761 svn_wc__db_status_t status;
10762 apr_hash_t *node_props;
10764 parent_relpath = relpath[0] ? svn_relpath_dirname(relpath, scratch_pool)
10767 svn_pool_clear(iterpool);
10769 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, relpath));
10771 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10774 return svn_error_createf(
10775 SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
10776 _("The node '%s' was not found."),
10777 path_for_error_message(wcroot, relpath,
10780 op_depth = svn_sqlite__column_int(stmt, 0);
10782 status = svn_sqlite__column_token(stmt, 3, presence_map);
10784 if (status != svn_wc__db_status_normal
10785 && status != svn_wc__db_status_incomplete)
10786 return svn_error_createf(
10787 SVN_ERR_WC_PATH_UNEXPECTED_STATUS, svn_sqlite__reset(stmt),
10788 _("The node '%s' has a status that has no properties."),
10789 path_for_error_message(wcroot, relpath,
10794 /* WORKING node. Nothing to check */
10796 else if (expected_parent_repos_relpath)
10798 const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10800 if (strcmp(expected_parent_repos_relpath, repos_relpath) != 0)
10802 /* The child of this node has a different parent than this node
10803 (It is "switched"), so we can stop here. Note that switched
10804 with the same parent is not interesting for us here. */
10805 SVN_ERR(svn_sqlite__reset(stmt));
10809 expected_parent_repos_relpath =
10810 svn_relpath_dirname(expected_parent_repos_relpath, scratch_pool);
10814 const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10816 expected_parent_repos_relpath =
10817 svn_relpath_dirname(repos_relpath, scratch_pool);
10821 && !svn_sqlite__column_is_null(stmt, 16))
10823 /* The node contains a cache. No reason to look further */
10824 SVN_ERR(svn_sqlite__column_iprops(&cached_iprops, stmt, 16,
10825 result_pool, iterpool));
10827 parent_relpath = NULL; /* Stop after this */
10830 SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 14,
10831 iterpool, iterpool));
10833 SVN_ERR(svn_sqlite__reset(stmt));
10835 /* If PARENT_ABSPATH is a parent of LOCAL_ABSPATH, then LOCAL_ABSPATH
10836 can inherit properties from it. */
10837 if (relpath != local_relpath)
10839 apr_hash_t *changed_props;
10841 SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10842 result_pool, iterpool));
10845 node_props = changed_props;
10846 else if (node_props)
10847 node_props = svn_prop_hash_dup(node_props, result_pool);
10849 if (node_props && apr_hash_count(node_props))
10851 /* If we only want PROPNAME filter out any other properties. */
10853 filter_unwanted_props(node_props, propname, iterpool);
10855 if (apr_hash_count(node_props))
10857 svn_prop_inherited_item_t *iprop_elt =
10858 apr_pcalloc(result_pool,
10859 sizeof(svn_prop_inherited_item_t));
10860 iprop_elt->path_or_url = svn_dirent_join(wcroot->abspath,
10864 iprop_elt->prop_hash = node_props;
10865 /* Build the output array in depth-first order. */
10866 svn_sort__array_insert(iprops, &iprop_elt, 0);
10870 else if (actual_props)
10872 apr_hash_t *changed_props;
10874 SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10875 result_pool, iterpool));
10878 *actual_props = changed_props;
10879 else if (node_props)
10880 *actual_props = svn_prop_hash_dup(node_props, result_pool);
10886 for (i = cached_iprops->nelts - 1; i >= 0; i--)
10888 svn_prop_inherited_item_t *cached_iprop =
10889 APR_ARRAY_IDX(cached_iprops, i, svn_prop_inherited_item_t *);
10891 /* An empty property hash in the iprops cache means there are no
10892 inherited properties. */
10893 if (apr_hash_count(cached_iprop->prop_hash) == 0)
10897 filter_unwanted_props(cached_iprop->prop_hash, propname,
10900 /* If we didn't filter everything then keep this iprop. */
10901 if (apr_hash_count(cached_iprop->prop_hash))
10902 svn_sort__array_insert(iprops, &cached_iprop, 0);
10906 if (actual_props && !*actual_props)
10907 *actual_props = apr_hash_make(result_pool);
10909 svn_pool_destroy(iterpool);
10910 return SVN_NO_ERROR;
10914 svn_wc__db_read_inherited_props(apr_array_header_t **iprops,
10915 apr_hash_t **actual_props,
10917 const char *local_abspath,
10918 const char *propname,
10919 apr_pool_t *result_pool,
10920 apr_pool_t *scratch_pool)
10922 svn_wc__db_wcroot_t *wcroot;
10923 const char *local_relpath;
10925 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10927 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10929 scratch_pool, scratch_pool));
10930 VERIFY_USABLE_WCROOT(wcroot);
10932 SVN_WC__DB_WITH_TXN(db_read_inherited_props(iprops, actual_props,
10933 wcroot, local_relpath, propname,
10934 result_pool, scratch_pool),
10937 return SVN_NO_ERROR;
10940 /* The body of svn_wc__db_get_children_with_cached_iprops().
10942 static svn_error_t *
10943 get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10944 svn_wc__db_wcroot_t *wcroot,
10945 const char *local_relpath,
10947 apr_pool_t *result_pool,
10948 apr_pool_t *scratch_pool)
10950 svn_sqlite__stmt_t *stmt;
10951 svn_boolean_t have_row;
10953 *iprop_paths = apr_hash_make(result_pool);
10955 /* First check if LOCAL_RELPATH itself has iprops */
10956 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10957 STMT_SELECT_IPROPS_NODE));
10958 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10959 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10963 const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10965 const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10966 relpath_with_cache,
10968 svn_hash_sets(*iprop_paths, abspath_with_cache,
10969 svn_sqlite__column_text(stmt, 1, result_pool));
10971 SVN_ERR(svn_sqlite__reset(stmt));
10973 if (depth == svn_depth_empty)
10974 return SVN_NO_ERROR;
10976 /* Now fetch information for children or all descendants */
10977 if (depth == svn_depth_files
10978 || depth == svn_depth_immediates)
10980 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10981 STMT_SELECT_IPROPS_CHILDREN));
10983 else /* Default to svn_depth_infinity. */
10985 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10986 STMT_SELECT_IPROPS_RECURSIVE));
10989 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10990 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10994 const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10996 const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10997 relpath_with_cache,
10999 svn_hash_sets(*iprop_paths, abspath_with_cache,
11000 svn_sqlite__column_text(stmt, 1, result_pool));
11001 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11004 SVN_ERR(svn_sqlite__reset(stmt));
11006 /* For depth files we should filter non files */
11007 if (depth == svn_depth_files)
11009 apr_hash_index_t *hi;
11010 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
11012 for (hi = apr_hash_first(scratch_pool, *iprop_paths);
11014 hi = apr_hash_next(hi))
11016 const char *child_abspath = apr_hash_this_key(hi);
11017 const char *child_relpath;
11018 svn_node_kind_t child_kind;
11020 svn_pool_clear(iterpool);
11022 child_relpath = svn_dirent_is_child(local_relpath, child_abspath,
11025 if (! child_relpath)
11027 continue; /* local_relpath itself */
11030 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &child_kind, NULL,
11031 NULL, NULL, NULL, NULL,
11032 NULL, NULL, NULL, NULL,
11033 NULL, NULL, NULL, NULL,
11034 wcroot, child_relpath,
11038 /* Filter if not a file */
11039 if (child_kind != svn_node_file)
11041 svn_hash_sets(*iprop_paths, child_abspath, NULL);
11045 svn_pool_destroy(iterpool);
11048 return SVN_NO_ERROR;
11052 svn_wc__db_get_children_with_cached_iprops(apr_hash_t **iprop_paths,
11054 const char *local_abspath,
11056 apr_pool_t *result_pool,
11057 apr_pool_t *scratch_pool)
11059 svn_wc__db_wcroot_t *wcroot;
11060 const char *local_relpath;
11062 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11064 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11065 local_abspath, scratch_pool,
11067 VERIFY_USABLE_WCROOT(wcroot);
11069 SVN_WC__DB_WITH_TXN(
11070 get_children_with_cached_iprops(iprop_paths, wcroot, local_relpath,
11071 depth, result_pool, scratch_pool),
11074 return SVN_NO_ERROR;
11078 svn_wc__db_read_children_of_working_node(const apr_array_header_t **children,
11080 const char *local_abspath,
11081 apr_pool_t *result_pool,
11082 apr_pool_t *scratch_pool)
11084 svn_wc__db_wcroot_t *wcroot;
11085 const char *local_relpath;
11087 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11089 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11091 scratch_pool, scratch_pool));
11092 VERIFY_USABLE_WCROOT(wcroot);
11094 return svn_error_trace(
11095 gather_children(children, wcroot, local_relpath,
11096 STMT_SELECT_WORKING_CHILDREN, -1,
11097 result_pool, scratch_pool));
11101 svn_wc__db_base_read_not_present_children(
11102 const apr_array_header_t **children,
11104 const char *local_abspath,
11105 apr_pool_t *result_pool,
11106 apr_pool_t *scratch_pool)
11108 svn_wc__db_wcroot_t *wcroot;
11109 const char *local_relpath;
11111 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11113 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11115 scratch_pool, scratch_pool));
11116 VERIFY_USABLE_WCROOT(wcroot);
11118 return svn_error_trace(
11119 gather_children(children, wcroot, local_relpath,
11120 STMT_SELECT_BASE_NOT_PRESENT_CHILDREN, -1,
11121 result_pool, scratch_pool));
11124 /* Helper for svn_wc__db_node_check_replace().
11126 static svn_error_t *
11127 check_replace_txn(svn_boolean_t *is_replace_root_p,
11128 svn_boolean_t *base_replace_p,
11129 svn_boolean_t *is_replace_p,
11130 svn_wc__db_wcroot_t *wcroot,
11131 const char *local_relpath,
11132 apr_pool_t *scratch_pool)
11134 svn_sqlite__stmt_t *stmt;
11135 svn_boolean_t have_row;
11136 svn_boolean_t is_replace = FALSE;
11137 int replaced_op_depth;
11138 svn_wc__db_status_t replaced_status;
11140 /* Our caller initialized the output values to FALSE */
11142 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11143 STMT_SELECT_NODE_INFO));
11145 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11147 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11150 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
11151 svn_sqlite__reset(stmt),
11152 _("The node '%s' was not found."),
11153 path_for_error_message(wcroot, local_relpath,
11157 svn_wc__db_status_t status;
11159 status = svn_sqlite__column_token(stmt, 3, presence_map);
11161 if (status != svn_wc__db_status_normal)
11162 return svn_error_trace(svn_sqlite__reset(stmt));
11165 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11168 return svn_error_trace(svn_sqlite__reset(stmt));
11170 replaced_status = svn_sqlite__column_token(stmt, 3, presence_map);
11172 /* If the layer below the add describes a not present or a deleted node,
11173 this is not a replacement. Deleted can only occur if an ancestor is
11174 the delete root. */
11175 if (replaced_status != svn_wc__db_status_not_present
11176 && replaced_status != svn_wc__db_status_excluded
11177 && replaced_status != svn_wc__db_status_server_excluded
11178 && replaced_status != svn_wc__db_status_base_deleted)
11182 *is_replace_p = TRUE;
11185 replaced_op_depth = svn_sqlite__column_int(stmt, 0);
11187 if (base_replace_p)
11189 int op_depth = svn_sqlite__column_int(stmt, 0);
11191 while (op_depth != 0 && have_row)
11193 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11196 op_depth = svn_sqlite__column_int(stmt, 0);
11199 if (have_row && op_depth == 0)
11201 svn_wc__db_status_t base_status;
11203 base_status = svn_sqlite__column_token(stmt, 3, presence_map);
11205 *base_replace_p = (base_status != svn_wc__db_status_not_present);
11209 SVN_ERR(svn_sqlite__reset(stmt));
11211 if (!is_replace_root_p || !is_replace)
11212 return SVN_NO_ERROR;
11214 if (replaced_status != svn_wc__db_status_base_deleted)
11216 int parent_op_depth;
11218 /* Check the current op-depth of the parent to see if we are a replacement
11220 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
11221 svn_relpath_dirname(local_relpath,
11224 SVN_ERR(svn_sqlite__step_row(stmt)); /* Parent must exist as 'normal' */
11226 parent_op_depth = svn_sqlite__column_int(stmt, 0);
11228 if (parent_op_depth >= replaced_op_depth)
11230 /* Did we replace inside our directory? */
11232 *is_replace_root_p = (parent_op_depth == replaced_op_depth);
11233 SVN_ERR(svn_sqlite__reset(stmt));
11234 return SVN_NO_ERROR;
11237 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11240 parent_op_depth = svn_sqlite__column_int(stmt, 0);
11242 SVN_ERR(svn_sqlite__reset(stmt));
11245 *is_replace_root_p = TRUE; /* Parent is no replacement */
11246 else if (parent_op_depth < replaced_op_depth)
11247 *is_replace_root_p = TRUE; /* Parent replaces a lower layer */
11248 /*else // No replacement root */
11251 return SVN_NO_ERROR;
11255 svn_wc__db_node_check_replace(svn_boolean_t *is_replace_root,
11256 svn_boolean_t *base_replace,
11257 svn_boolean_t *is_replace,
11259 const char *local_abspath,
11260 apr_pool_t *scratch_pool)
11262 svn_wc__db_wcroot_t *wcroot;
11263 const char *local_relpath;
11265 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11267 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11269 scratch_pool, scratch_pool));
11270 VERIFY_USABLE_WCROOT(wcroot);
11272 if (is_replace_root)
11273 *is_replace_root = FALSE;
11275 *base_replace = FALSE;
11277 *is_replace = FALSE;
11279 if (local_relpath[0] == '\0')
11280 return SVN_NO_ERROR; /* Working copy root can't be replaced */
11282 SVN_WC__DB_WITH_TXN(
11283 check_replace_txn(is_replace_root, base_replace, is_replace,
11284 wcroot, local_relpath, scratch_pool),
11287 return SVN_NO_ERROR;
11291 svn_wc__db_read_children(const apr_array_header_t **children,
11293 const char *local_abspath,
11294 apr_pool_t *result_pool,
11295 apr_pool_t *scratch_pool)
11297 svn_wc__db_wcroot_t *wcroot;
11298 const char *local_relpath;
11300 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11302 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11304 scratch_pool, scratch_pool));
11305 VERIFY_USABLE_WCROOT(wcroot);
11307 return gather_children(children, wcroot, local_relpath,
11308 STMT_SELECT_NODE_CHILDREN, -1,
11309 result_pool, scratch_pool);
11313 /* Implementation of svn_wc__db_global_relocate */
11314 static svn_error_t *
11315 relocate_txn(svn_wc__db_wcroot_t *wcroot,
11316 const char *local_relpath,
11317 const char *repos_root_url,
11318 apr_pool_t *scratch_pool)
11320 svn_sqlite__stmt_t *stmt;
11321 apr_int64_t new_repos_id;
11322 const char *local_dir_relpath;
11323 svn_wc__db_status_t status;
11324 const char *repos_uuid;
11325 svn_boolean_t have_base_node;
11326 apr_int64_t old_repos_id;
11328 local_dir_relpath = local_relpath;
11330 SVN_ERR(read_info(&status,
11331 NULL, NULL, NULL, &old_repos_id,
11332 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11333 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11335 &have_base_node, NULL, NULL,
11336 wcroot, local_relpath,
11337 scratch_pool, scratch_pool));
11339 if (status == svn_wc__db_status_excluded)
11341 /* The parent cannot be excluded, so look at the parent and then
11342 adjust the relpath */
11343 const char *parent_relpath = svn_relpath_dirname(local_dir_relpath,
11345 SVN_ERR(read_info(&status,
11346 NULL, NULL, NULL, &old_repos_id,
11347 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11348 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11351 wcroot, parent_relpath,
11352 scratch_pool, scratch_pool));
11353 local_dir_relpath = parent_relpath;
11356 if (old_repos_id == INVALID_REPOS_ID)
11358 /* Do we need to support relocating something that is
11359 added/deleted/excluded without relocating the parent? If not
11360 then perhaps relpath, root_url and uuid should be passed down
11361 to the children so that they don't have to scan? */
11363 if (status == svn_wc__db_status_deleted)
11365 const char *work_del_relpath;
11367 SVN_ERR(scan_deletion(NULL, NULL,
11368 &work_del_relpath, NULL,
11369 wcroot, local_dir_relpath,
11372 if (work_del_relpath)
11374 /* Deleted within a copy/move */
11376 /* The parent of the delete is added. */
11377 status = svn_wc__db_status_added;
11378 local_dir_relpath = svn_relpath_dirname(work_del_relpath,
11383 if (status == svn_wc__db_status_added)
11385 SVN_ERR(scan_addition(NULL, NULL, NULL, &old_repos_id,
11386 NULL, NULL, NULL, NULL, NULL, NULL,
11387 wcroot, local_dir_relpath,
11388 scratch_pool, scratch_pool));
11391 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL,
11393 NULL, NULL, NULL, NULL, NULL,
11394 NULL, NULL, NULL, NULL, NULL,
11395 wcroot, local_dir_relpath,
11396 scratch_pool, scratch_pool));
11399 SVN_ERR(svn_wc__db_fetch_repos_info(NULL, &repos_uuid, wcroot,
11400 old_repos_id, scratch_pool));
11401 SVN_ERR_ASSERT(repos_uuid); /* This function affects all the children of the given local_relpath,
11402 but the way that it does this is through the repos inheritance mechanism.
11403 So, we only need to rewrite the repos_id of the given local_relpath,
11404 as well as any children with a non-null repos_id, as well as various
11405 repos_id fields in the locks and working_node tables.
11408 /* Get the repos_id for the new repository. */
11409 SVN_ERR(create_repos_id(&new_repos_id, repos_root_url, repos_uuid,
11410 wcroot->sdb, scratch_pool));
11412 /* Set the (base and working) repos_ids and clear the dav_caches */
11413 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11414 STMT_RECURSIVE_UPDATE_NODE_REPO));
11415 SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
11416 old_repos_id, new_repos_id));
11417 SVN_ERR(svn_sqlite__step_done(stmt));
11419 if (have_base_node)
11421 /* Update any locks for the root or its children. */
11422 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11423 STMT_UPDATE_LOCK_REPOS_ID));
11424 SVN_ERR(svn_sqlite__bindf(stmt, "ii", old_repos_id, new_repos_id));
11425 SVN_ERR(svn_sqlite__step_done(stmt));
11428 return SVN_NO_ERROR;
11433 svn_wc__db_global_relocate(svn_wc__db_t *db,
11434 const char *local_dir_abspath,
11435 const char *repos_root_url,
11436 apr_pool_t *scratch_pool)
11438 svn_wc__db_wcroot_t *wcroot;
11439 const char *local_relpath;
11441 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
11442 /* ### assert that we were passed a directory? */
11444 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
11445 db, local_dir_abspath, scratch_pool, scratch_pool));
11446 VERIFY_USABLE_WCROOT(wcroot);
11448 SVN_WC__DB_WITH_TXN(
11449 relocate_txn(wcroot, local_relpath, repos_root_url, scratch_pool),
11452 SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_infinity,
11455 return SVN_NO_ERROR;
11459 /* Helper for commit_node()
11460 Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of
11461 (WCROOT, LOCAL_RELPATH), directly if its BASE row exists or implied from
11462 its parent's BASE row if not. In the latter case, error if the parent
11463 BASE row does not exist. */
11464 static svn_error_t *
11465 determine_commit_repos_info(apr_int64_t *repos_id,
11466 const char **repos_relpath,
11467 svn_wc__db_wcroot_t *wcroot,
11468 const char *local_relpath,
11469 apr_pool_t *result_pool,
11470 apr_pool_t *scratch_pool)
11472 svn_sqlite__stmt_t *stmt;
11473 svn_boolean_t have_row;
11476 /* Prefer the current node's repository information. */
11477 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11478 STMT_SELECT_NODE_INFO));
11479 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11480 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11483 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
11484 svn_sqlite__reset(stmt),
11485 _("The node '%s' was not found."),
11486 path_for_error_message(wcroot, local_relpath,
11489 op_depth = svn_sqlite__column_int(stmt, 0);
11493 svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 3,
11496 if (presence == svn_wc__db_status_base_deleted)
11498 SVN_ERR(svn_sqlite__step_row(stmt)); /* There must be a row */
11499 op_depth = svn_sqlite__column_int(stmt, 0);
11503 const char *parent_repos_relpath;
11504 const char *parent_relpath;
11507 SVN_ERR(svn_sqlite__reset(stmt));
11509 /* The repository relative path of an add/copy is based on its
11510 ancestor, not on the shadowed base layer.
11512 As this function is only used from the commit processing we know
11513 the parent directory has only a BASE row, so we can just obtain
11514 the information directly by recursing (once!) */
11516 svn_relpath_split(&parent_relpath, &name, local_relpath,
11519 SVN_ERR(determine_commit_repos_info(repos_id, &parent_repos_relpath,
11520 wcroot, parent_relpath,
11521 scratch_pool, scratch_pool));
11523 *repos_relpath = svn_relpath_join(parent_repos_relpath, name,
11525 return SVN_NO_ERROR;
11530 SVN_ERR_ASSERT(op_depth == 0); /* And that row must be BASE */
11532 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1));
11533 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 2));
11535 *repos_id = svn_sqlite__column_int64(stmt, 1);
11536 *repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
11538 return svn_error_trace(svn_sqlite__reset(stmt));
11541 static svn_error_t *
11542 moved_descendant_collect(apr_hash_t **map,
11543 svn_wc__db_wcroot_t *wcroot,
11544 const char *local_relpath,
11546 apr_pool_t *result_pool,
11547 apr_pool_t *scratch_pool)
11549 svn_sqlite__stmt_t *stmt;
11550 svn_boolean_t have_row;
11554 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11555 STMT_SELECT_MOVED_DESCENDANTS_SRC));
11556 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
11560 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11562 return svn_error_trace(svn_sqlite__reset(stmt));
11564 /* Find all moved descendants. Key them on target, because that is
11568 const char *src_relpath = svn_sqlite__column_text(stmt, 1, result_pool);
11569 const char *to_relpath = svn_sqlite__column_text(stmt, 4, result_pool);
11572 *map = apr_hash_make(result_pool);
11574 svn_hash_sets(*map, to_relpath, src_relpath);
11576 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11578 SVN_ERR(svn_sqlite__reset(stmt));
11580 return SVN_NO_ERROR;
11583 /* Helper for svn_wc__db_global_commit()
11585 Makes local_relpath and all its descendants at the same op-depth represent
11586 the copy origin repos_id:repos_relpath@revision.
11588 This code is only valid to fix-up a move from an old location, to a new
11589 location during a commit.
11592 * local_relpath is not the working copy root (can't be moved)
11593 * repos_relpath is not the repository root (can't be moved)
11595 static svn_error_t *
11596 moved_descendant_commit(svn_wc__db_wcroot_t *wcroot,
11597 const char *local_relpath,
11598 apr_int64_t repos_id,
11599 const char *repos_relpath,
11600 svn_revnum_t revision,
11601 apr_hash_t *children,
11602 apr_pool_t *scratch_pool)
11604 apr_pool_t *iterpool;
11605 svn_sqlite__stmt_t *stmt;
11606 apr_hash_index_t *hi;
11608 SVN_ERR_ASSERT(*local_relpath != '\0'
11609 && *repos_relpath != '\0');
11612 return SVN_NO_ERROR;
11614 /* Then update them */
11615 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11616 STMT_COMMIT_UPDATE_ORIGIN));
11618 iterpool = svn_pool_create(scratch_pool);
11619 for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
11621 const char *src_relpath = apr_hash_this_val(hi);
11622 const char *to_relpath = apr_hash_this_key(hi);
11623 const char *new_repos_relpath;
11624 int to_op_depth = relpath_depth(to_relpath);
11628 svn_pool_clear(iterpool);
11630 SVN_ERR_ASSERT(to_op_depth > 0);
11632 new_repos_relpath = svn_relpath_join(
11634 svn_relpath_skip_ancestor(local_relpath,
11638 SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11644 SVN_ERR(svn_sqlite__update(&affected, stmt));
11647 /* Enable in release code?
11648 Broken moves are not fatal yet, but this assertion would break
11650 SVN_ERR_ASSERT(affected >= 1); /* If this fails there is no move dest */
11653 SVN_ERR(moved_descendant_collect(&map, wcroot, to_relpath, to_op_depth,
11654 iterpool, iterpool));
11655 SVN_ERR(moved_descendant_commit(wcroot, to_relpath,
11656 repos_id, new_repos_relpath, revision,
11660 svn_pool_destroy(iterpool);
11661 return SVN_NO_ERROR;
11664 /* Helper for svn_wc__db_global_commit()
11666 Moves all nodes below LOCAL_RELPATH from op-depth OP_DEPTH to op-depth 0
11667 (BASE), setting their presence to 'not-present' if their presence wasn't
11670 Makes all nodes below LOCAL_RELPATH represent the descendants of repository
11671 location repos_id:repos_relpath@revision.
11674 * local_relpath is not the working copy root (can't be replaced)
11675 * repos_relpath is not the repository root (can't be replaced)
11677 static svn_error_t *
11678 descendant_commit(svn_wc__db_wcroot_t *wcroot,
11679 const char *local_relpath,
11681 apr_int64_t repos_id,
11682 const char *repos_relpath,
11683 svn_revnum_t revision,
11684 apr_pool_t *scratch_pool)
11686 svn_sqlite__stmt_t *stmt;
11688 SVN_ERR_ASSERT(*local_relpath != '\0'
11689 && *repos_relpath != '\0');
11691 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11692 STMT_COMMIT_DESCENDANTS_TO_BASE));
11694 SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11701 SVN_ERR(svn_sqlite__update(NULL, stmt));
11703 return SVN_NO_ERROR;
11706 /* The body of svn_wc__db_global_commit().
11708 static svn_error_t *
11709 commit_node(svn_wc__db_wcroot_t *wcroot,
11710 const char *local_relpath,
11711 svn_revnum_t new_revision,
11712 svn_revnum_t changed_rev,
11713 apr_time_t changed_date,
11714 const char *changed_author,
11715 const svn_checksum_t *new_checksum,
11716 apr_hash_t *new_dav_cache,
11717 svn_boolean_t keep_changelist,
11718 svn_boolean_t no_unlock,
11719 const svn_skel_t *work_items,
11720 apr_pool_t *scratch_pool)
11722 svn_sqlite__stmt_t *stmt_info;
11723 svn_sqlite__stmt_t *stmt_act;
11724 svn_boolean_t have_act;
11725 svn_string_t prop_blob = { 0 };
11726 svn_string_t inherited_prop_blob = { 0 };
11727 const char *changelist = NULL;
11728 const char *parent_relpath;
11729 svn_wc__db_status_t new_presence;
11730 svn_node_kind_t new_kind;
11731 const char *new_depth_str = NULL;
11732 svn_sqlite__stmt_t *stmt;
11733 apr_int64_t repos_id;
11734 const char *repos_relpath;
11736 svn_wc__db_status_t old_presence;
11737 svn_boolean_t moved_here;
11739 /* If we are adding a file or directory, then we need to get
11740 repository information from the parent node since "this node" does
11743 For existing nodes, we should retain the (potentially-switched)
11744 repository information. */
11745 SVN_ERR(determine_commit_repos_info(&repos_id, &repos_relpath,
11746 wcroot, local_relpath,
11747 scratch_pool, scratch_pool));
11749 /* ### is it better to select only the data needed? */
11750 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
11751 STMT_SELECT_NODE_INFO));
11752 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
11753 SVN_ERR(svn_sqlite__step_row(stmt_info));
11755 SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
11756 STMT_SELECT_ACTUAL_NODE));
11757 SVN_ERR(svn_sqlite__bindf(stmt_act, "is",
11758 wcroot->wc_id, local_relpath));
11759 SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
11761 /* There should be something to commit! */
11763 op_depth = svn_sqlite__column_int(stmt_info, 0);
11765 /* Figure out the new node's kind. It will be whatever is in WORKING_NODE,
11766 or there will be a BASE_NODE that has it. */
11767 old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map);
11768 new_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
11770 /* What will the new depth be? */
11771 if (new_kind == svn_node_dir)
11772 new_depth_str = svn_sqlite__column_text(stmt_info, 11, scratch_pool);
11774 /* Check that the repository information is not being changed. */
11777 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 1));
11778 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 2));
11780 /* A commit cannot change these values. */
11781 SVN_ERR_ASSERT(repos_id == svn_sqlite__column_int64(stmt_info, 1));
11782 SVN_ERR_ASSERT(strcmp(repos_relpath,
11783 svn_sqlite__column_text(stmt_info, 2, NULL)) == 0);
11786 if (old_presence != svn_wc__db_status_base_deleted)
11788 /* Find the appropriate new properties -- ACTUAL overrides any properties
11789 in WORKING that arrived as part of a copy/move.
11791 Note: we'll keep them as a big blob of data, rather than
11792 deserialize/serialize them. */
11794 prop_blob.data = svn_sqlite__column_blob(stmt_act, 1, &prop_blob.len,
11796 if (prop_blob.data == NULL)
11797 prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len,
11800 inherited_prop_blob.data = svn_sqlite__column_blob(
11802 &inherited_prop_blob.len,
11805 if (keep_changelist && have_act)
11806 changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool);
11808 moved_here = svn_sqlite__column_int(stmt_info, 15);
11812 moved_here = FALSE;
11816 /* ### other stuff? */
11818 SVN_ERR(svn_sqlite__reset(stmt_info));
11819 SVN_ERR(svn_sqlite__reset(stmt_act));
11825 SVN_ERR_ASSERT(op_depth == relpath_depth(local_relpath));
11827 /* First clear the moves that we are going to delete in a bit */
11829 apr_hash_t *old_moves;
11830 apr_hash_index_t *hi;
11831 SVN_ERR(moved_descendant_collect(&old_moves, wcroot, local_relpath, 0,
11832 scratch_pool, scratch_pool));
11835 for (hi = apr_hash_first(scratch_pool, old_moves);
11836 hi; hi = apr_hash_next(hi))
11838 SVN_ERR(clear_moved_here(wcroot, apr_hash_this_key(hi),
11843 /* This removes all layers of this node and at the same time determines
11844 if we need to remove shadowed layers below our descendants. */
11846 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11847 STMT_DELETE_NODE_ALL_LAYERS));
11848 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11849 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
11851 if (affected_rows > 1)
11853 /* We commit a shadowing operation
11855 1) Remove all shadowed nodes
11856 2) And remove all nodes that have a base-deleted as lowest layer,
11857 because 1) removed that layer */
11859 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11860 STMT_DELETE_SHADOWED_RECURSIVE));
11862 SVN_ERR(svn_sqlite__bindf(stmt,
11868 SVN_ERR(svn_sqlite__step_done(stmt));
11871 /* Note that while these two calls look so similar that they might
11872 be integrated, they really affect a different op-depth and
11873 completely different nodes (via a different recursion pattern). */
11875 if (old_presence != svn_wc__db_status_base_deleted)
11877 /* Collapse descendants of the current op_depth to layer 0,
11878 this includes moved-from/to clearing */
11879 SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth,
11880 repos_id, repos_relpath, new_revision,
11884 if (old_presence != svn_wc__db_status_base_deleted)
11886 apr_hash_t *moves = NULL;
11888 SVN_ERR(moved_descendant_collect(&moves, wcroot, local_relpath, 0,
11889 scratch_pool, scratch_pool));
11891 /* And make the recorded local moves represent moves of the node we
11893 SVN_ERR(moved_descendant_commit(wcroot, local_relpath,
11894 repos_id, repos_relpath, new_revision,
11895 moves, scratch_pool));
11900 /* This node is no longer modified, so no node was moved here */
11901 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11902 STMT_CLEAR_MOVED_TO_FROM_DEST));
11903 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
11906 SVN_ERR(svn_sqlite__step_done(stmt));
11909 /* Update or add the BASE_NODE row with all the new information. */
11911 if (*local_relpath == '\0')
11912 parent_relpath = NULL;
11914 parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
11916 /* Preserve any incomplete status */
11917 if (old_presence != svn_wc__db_status_base_deleted)
11919 new_presence = (old_presence == svn_wc__db_status_incomplete
11920 ? svn_wc__db_status_incomplete
11921 : svn_wc__db_status_normal);
11923 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11924 STMT_APPLY_CHANGES_TO_BASE_NODE));
11925 /* symlink_target not yet used */
11926 SVN_ERR(svn_sqlite__bindf(stmt, "issisrtstrisnbn",
11927 wcroot->wc_id, local_relpath,
11932 presence_map, new_presence,
11934 kind_map, new_kind,
11938 prop_blob.data, prop_blob.len));
11940 SVN_ERR(svn_sqlite__bind_checksum(stmt, 13, new_checksum,
11942 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, new_dav_cache,
11944 if (inherited_prop_blob.data != NULL)
11946 SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data,
11947 inherited_prop_blob.len));
11950 SVN_ERR(svn_sqlite__step_done(stmt));
11954 struct insert_base_baton_t ibb;
11957 ibb.repos_id = repos_id;
11958 ibb.status = svn_wc__db_status_not_present;
11959 ibb.kind = new_kind;
11960 ibb.repos_relpath = repos_relpath;
11961 ibb.revision = new_revision;
11963 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
11965 keep_changelist = FALSE; /* Nothing there */
11970 if (keep_changelist && changelist != NULL)
11972 /* The user told us to keep the changelist. Replace the row in
11973 ACTUAL_NODE with the basic keys and the changelist. */
11974 SVN_ERR(svn_sqlite__get_statement(
11975 &stmt, wcroot->sdb,
11976 STMT_RESET_ACTUAL_WITH_CHANGELIST));
11977 SVN_ERR(svn_sqlite__bindf(stmt, "isss",
11978 wcroot->wc_id, local_relpath,
11979 svn_relpath_dirname(local_relpath,
11982 SVN_ERR(svn_sqlite__step_done(stmt));
11986 /* Toss the ACTUAL_NODE row. */
11987 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11988 STMT_DELETE_ACTUAL_NODE));
11989 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11990 SVN_ERR(svn_sqlite__step_done(stmt));
11996 svn_sqlite__stmt_t *lock_stmt;
11997 svn_boolean_t op_root = (op_depth > 0
11998 && (relpath_depth(local_relpath) == op_depth));
12000 /* If we are committing an add of a delete, we can assume we own
12001 all locks at or below REPOS_RELPATH (or the server would have
12002 denied the commit). As we must have passed these to the server
12003 we can now safely remove them.
12005 SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
12007 ? STMT_DELETE_LOCK_RECURSIVELY
12008 : STMT_DELETE_LOCK));
12009 SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
12010 SVN_ERR(svn_sqlite__step_done(lock_stmt));
12013 /* Install any work items into the queue, as part of this transaction. */
12014 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
12016 return SVN_NO_ERROR;
12021 svn_wc__db_global_commit(svn_wc__db_t *db,
12022 const char *local_abspath,
12023 svn_revnum_t new_revision,
12024 svn_revnum_t changed_revision,
12025 apr_time_t changed_date,
12026 const char *changed_author,
12027 const svn_checksum_t *new_checksum,
12028 apr_hash_t *new_dav_cache,
12029 svn_boolean_t keep_changelist,
12030 svn_boolean_t no_unlock,
12031 const svn_skel_t *work_items,
12032 apr_pool_t *scratch_pool)
12034 const char *local_relpath;
12035 svn_wc__db_wcroot_t *wcroot;
12037 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12038 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
12040 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12041 local_abspath, scratch_pool, scratch_pool));
12042 VERIFY_USABLE_WCROOT(wcroot);
12044 SVN_WC__DB_WITH_TXN(
12045 commit_node(wcroot, local_relpath,
12046 new_revision, changed_revision, changed_date, changed_author,
12047 new_checksum, new_dav_cache, keep_changelist,
12048 no_unlock, work_items, scratch_pool),
12051 /* We *totally* monkeyed the entries. Toss 'em. */
12052 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
12054 return SVN_NO_ERROR;
12059 svn_wc__db_global_update(svn_wc__db_t *db,
12060 const char *local_abspath,
12061 svn_node_kind_t new_kind,
12062 const char *new_repos_relpath,
12063 svn_revnum_t new_revision,
12064 const apr_hash_t *new_props,
12065 svn_revnum_t new_changed_rev,
12066 apr_time_t new_changed_date,
12067 const char *new_changed_author,
12068 const apr_array_header_t *new_children,
12069 const svn_checksum_t *new_checksum,
12070 const char *new_target,
12071 const apr_hash_t *new_dav_cache,
12072 const svn_skel_t *conflict,
12073 const svn_skel_t *work_items,
12074 apr_pool_t *scratch_pool)
12079 svn_wc__db_wcroot_t *wcroot;
12080 const char *local_relpath;
12082 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12083 /* ### allow NULL for NEW_REPOS_RELPATH to indicate "no change"? */
12084 SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
12085 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
12086 SVN_ERR_ASSERT(new_props != NULL);
12087 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_changed_rev));
12088 SVN_ERR_ASSERT((new_children != NULL
12089 && new_checksum == NULL
12090 && new_target == NULL)
12091 || (new_children == NULL
12092 && new_checksum != NULL
12093 && new_target == NULL)
12094 || (new_children == NULL
12095 && new_checksum == NULL
12096 && new_target != NULL));
12098 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12099 local_abspath, scratch_pool, scratch_pool));
12100 VERIFY_USABLE_WCROOT(wcroot);
12102 SVN_WC__DB_WITH_TXN(
12103 update_node(wcroot, local_relpath,
12104 new_repos_relpath, new_revision, new_props,
12105 new_changed_rev, new_changed_date, new_changed_author,
12106 new_children, new_checksum, new_target,
12107 conflict, work_items, scratch_pool),
12110 /* We *totally* monkeyed the entries. Toss 'em. */
12111 SVN_ERR(flush_entries(wcroot, local_abspath, scratch_pool));
12113 return SVN_NO_ERROR;
12117 /* Sets a base nodes revision, repository relative path, and/or inherited
12118 propertis. If LOCAL_ABSPATH's rev (REV) is valid, set its revision. If
12119 SET_REPOS_RELPATH is TRUE set its repository relative path to REPOS_RELPATH
12120 (and make sure its REPOS_ID is still valid). If IPROPS is not NULL set its
12121 inherited properties to IPROPS, if IPROPS is NULL then clear any the iprops
12122 cache for the base node.
12124 static svn_error_t *
12125 db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot,
12126 const char *local_relpath,
12127 apr_array_header_t *iprops,
12129 svn_boolean_t set_repos_relpath,
12130 const char *repos_relpath,
12131 apr_int64_t repos_id,
12132 apr_pool_t *scratch_pool)
12134 svn_sqlite__stmt_t *stmt;
12136 SVN_ERR(flush_entries(wcroot,
12137 svn_dirent_join(wcroot->abspath, local_relpath,
12139 svn_depth_empty, scratch_pool));
12142 if (SVN_IS_VALID_REVNUM(rev))
12144 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12145 STMT_UPDATE_BASE_REVISION));
12147 SVN_ERR(svn_sqlite__bindf(stmt, "isr", wcroot->wc_id, local_relpath,
12150 SVN_ERR(svn_sqlite__step_done(stmt));
12153 if (set_repos_relpath)
12155 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12156 STMT_UPDATE_BASE_REPOS));
12158 SVN_ERR(svn_sqlite__bindf(stmt, "isis", wcroot->wc_id, local_relpath,
12159 repos_id, repos_relpath));
12161 SVN_ERR(svn_sqlite__step_done(stmt));
12164 /* Set or clear iprops. */
12165 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12166 STMT_UPDATE_IPROP));
12167 SVN_ERR(svn_sqlite__bindf(stmt, "is",
12170 SVN_ERR(svn_sqlite__bind_iprops(stmt, 3, iprops, scratch_pool));
12171 SVN_ERR(svn_sqlite__step_done(stmt));
12173 return SVN_NO_ERROR;
12176 /* The main body of bump_revisions_post_update().
12178 * Tweak the information for LOCAL_RELPATH in WCROOT. If NEW_REPOS_RELPATH is
12179 * non-NULL update the entry to the new url specified by NEW_REPOS_RELPATH,
12180 * NEW_REPOS_ID. If NEW_REV is valid, make this the node's working revision.
12182 * NODE_STATUS, NODE_KIND, NODE_REVISION and NODE_REPOS_RELPATH represent the
12183 * values as stored currently in WCROOT for LOCAL_RELPATH.
12185 * If WCROOT_IPROPS is not NULL it is a hash mapping const char * absolute
12186 * working copy paths to depth-first ordered arrays of
12187 * svn_prop_inherited_item_t * structures. If the absolute path equivalent
12188 * of LOCAL_RELPATH exists in WCROOT_IPROPS, then set the hashed value as the
12189 * node's inherited properties.
12191 * Unless S_ROOT is TRUE the tweaks might cause the node for LOCAL_ABSPATH to
12192 * be removed from the WC; if IS_ROOT is TRUE this will not happen.
12194 static svn_error_t *
12195 bump_node_revision(svn_wc__db_wcroot_t *wcroot,
12196 const char *local_relpath,
12197 svn_wc__db_status_t node_status,
12198 svn_node_kind_t node_kind,
12199 svn_revnum_t node_revision,
12200 const char *node_repos_relpath,
12201 apr_int64_t new_repos_id,
12202 const char *new_repos_relpath,
12203 svn_revnum_t new_rev,
12205 apr_hash_t *exclude_relpaths,
12206 apr_hash_t *wcroot_iprops,
12207 svn_boolean_t is_root,
12208 svn_boolean_t skip_when_dir,
12210 apr_pool_t *scratch_pool)
12212 apr_pool_t *iterpool;
12213 apr_hash_t *children;
12214 apr_hash_index_t *hi;
12215 svn_boolean_t set_repos_relpath = FALSE;
12216 svn_depth_t depth_below_here = depth;
12217 apr_array_header_t *iprops = NULL;
12219 if (new_repos_relpath != NULL
12220 && strcmp(node_repos_relpath, new_repos_relpath))
12221 set_repos_relpath = TRUE;
12224 iprops = svn_hash_gets(wcroot_iprops,
12225 svn_dirent_join(wcroot->abspath, local_relpath,
12229 || set_repos_relpath
12230 || (SVN_IS_VALID_REVNUM(new_rev) && new_rev != node_revision))
12232 SVN_ERR(db_op_set_rev_repos_relpath_iprops(wcroot, local_relpath,
12241 if (depth <= svn_depth_empty
12242 || node_kind != svn_node_dir
12243 || node_status == svn_wc__db_status_server_excluded
12244 || node_status == svn_wc__db_status_excluded
12245 || node_status == svn_wc__db_status_not_present)
12246 return SVN_NO_ERROR;
12248 /* And now recurse over the children */
12250 depth_below_here = depth;
12252 if (depth == svn_depth_immediates || depth == svn_depth_files)
12253 depth_below_here = svn_depth_empty;
12255 iterpool = svn_pool_create(scratch_pool);
12257 SVN_ERR(base_get_children_info(&children, wcroot, local_relpath, 0,
12258 scratch_pool, iterpool));
12259 for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
12261 const char *child_basename = apr_hash_this_key(hi);
12262 const struct svn_wc__db_base_info_t *child_info;
12263 const char *child_local_relpath;
12264 const char *child_repos_relpath = NULL;
12266 svn_pool_clear(iterpool);
12268 child_info = apr_hash_this_val(hi);
12270 if (child_info->update_root && child_info->kind == svn_node_file)
12271 continue; /* Skip file externals */
12273 if (depth < svn_depth_immediates && child_info->kind == svn_node_dir)
12274 continue; /* Skip directories */
12276 child_local_relpath = svn_relpath_join(local_relpath, child_basename,
12279 /* Don't touch nodes that can't be touched via the exclude list */
12280 if (svn_hash_gets(exclude_relpaths, child_local_relpath))
12283 /* If the node is still marked 'not-present', then the server did not
12284 re-add it. So it's really gone in this revision, thus we remove the
12287 If the node is still marked 'server-excluded' and yet is not the same
12288 revision as new_rev, then the server did not re-add it, nor
12289 re-server-exclude it, so we can remove the node. */
12290 if (child_info->status == svn_wc__db_status_not_present
12291 || (child_info->status == svn_wc__db_status_server_excluded &&
12292 child_info->revnum != new_rev))
12294 svn_sqlite__stmt_t *stmt;
12295 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12296 STMT_DELETE_BASE_NODE));
12297 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, child_local_relpath));
12298 SVN_ERR(svn_sqlite__step_done(stmt));
12302 /* Derive the new URL for the current (child) entry */
12303 if (new_repos_relpath)
12304 child_repos_relpath = svn_relpath_join(new_repos_relpath,
12305 child_basename, iterpool);
12307 SVN_ERR(bump_node_revision(wcroot, child_local_relpath,
12308 child_info->status,
12310 child_info->revnum,
12311 child_info->repos_relpath,
12313 child_repos_relpath, new_rev,
12315 exclude_relpaths, wcroot_iprops,
12316 FALSE /* is_root */,
12317 (depth < svn_depth_immediates), db,
12322 svn_pool_destroy(iterpool);
12324 return SVN_NO_ERROR;
12327 /* Helper for svn_wc__db_op_bump_revisions_post_update().
12329 static svn_error_t *
12330 bump_revisions_post_update(svn_wc__db_wcroot_t *wcroot,
12331 const char *local_relpath,
12334 const char *new_repos_relpath,
12335 const char *new_repos_root_url,
12336 const char *new_repos_uuid,
12337 svn_revnum_t new_revision,
12338 apr_hash_t *exclude_relpaths,
12339 apr_hash_t *wcroot_iprops,
12340 svn_boolean_t empty_update,
12341 svn_wc_notify_func2_t notify_func,
12342 void *notify_baton,
12343 apr_pool_t *scratch_pool)
12345 svn_wc__db_status_t status;
12346 svn_node_kind_t kind;
12348 apr_int64_t new_repos_id = INVALID_REPOS_ID;
12349 svn_revnum_t revision;
12350 const char *repos_relpath;
12352 err = svn_wc__db_base_get_info_internal(&status, &kind, &revision,
12353 &repos_relpath, NULL,
12354 NULL, NULL, NULL, NULL, NULL, NULL,
12355 NULL, NULL, NULL, NULL,
12356 wcroot, local_relpath,
12357 scratch_pool, scratch_pool);
12358 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
12360 svn_error_clear(err);
12361 return SVN_NO_ERROR;
12368 case svn_wc__db_status_excluded:
12369 case svn_wc__db_status_server_excluded:
12370 case svn_wc__db_status_not_present:
12371 return SVN_NO_ERROR;
12373 /* Explicitly ignore other statii */
12378 if (new_repos_root_url != NULL)
12379 SVN_ERR(create_repos_id(&new_repos_id, new_repos_root_url,
12381 wcroot->sdb, scratch_pool));
12383 SVN_ERR(bump_node_revision(wcroot, local_relpath,
12384 status, kind, revision, repos_relpath,
12386 new_repos_relpath, new_revision,
12387 depth, exclude_relpaths,
12389 TRUE /* is_root */, FALSE, db,
12392 /* ### TODO: Use empty_update flag for change knowledge */
12393 SVN_ERR(svn_wc__db_bump_moved_away(wcroot, local_relpath, depth, db,
12396 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, SVN_INVALID_REVNUM,
12397 SVN_INVALID_REVNUM, notify_func,
12398 notify_baton, scratch_pool));
12400 return SVN_NO_ERROR;
12404 svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db,
12405 const char *local_abspath,
12407 const char *new_repos_relpath,
12408 const char *new_repos_root_url,
12409 const char *new_repos_uuid,
12410 svn_revnum_t new_revision,
12411 apr_hash_t *exclude_relpaths,
12412 apr_hash_t *wcroot_iprops,
12413 svn_boolean_t empty_update,
12414 svn_wc_notify_func2_t notify_func,
12415 void *notify_baton,
12416 apr_pool_t *scratch_pool)
12418 const char *local_relpath;
12419 svn_wc__db_wcroot_t *wcroot;
12421 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12422 local_abspath, scratch_pool, scratch_pool));
12424 VERIFY_USABLE_WCROOT(wcroot);
12426 if (svn_hash_gets(exclude_relpaths, local_relpath))
12427 return SVN_NO_ERROR;
12429 if (depth == svn_depth_unknown)
12430 depth = svn_depth_infinity;
12432 SVN_WC__DB_WITH_TXN(
12433 bump_revisions_post_update(wcroot, local_relpath, db,
12434 depth, new_repos_relpath, new_repos_root_url,
12435 new_repos_uuid, new_revision,
12436 exclude_relpaths, wcroot_iprops, empty_update,
12437 notify_func, notify_baton, scratch_pool),
12440 return SVN_NO_ERROR;
12443 /* The body of svn_wc__db_lock_add().
12445 static svn_error_t *
12446 lock_add_txn(svn_wc__db_wcroot_t *wcroot,
12447 const char *local_relpath,
12448 const svn_wc__db_lock_t *lock,
12449 apr_pool_t *scratch_pool)
12451 svn_sqlite__stmt_t *stmt;
12452 const char *repos_relpath;
12453 apr_int64_t repos_id;
12455 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12456 &repos_relpath, &repos_id,
12457 NULL, NULL, NULL, NULL, NULL,
12458 NULL, NULL, NULL, NULL, NULL,
12459 wcroot, local_relpath,
12460 scratch_pool, scratch_pool));
12462 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_LOCK));
12463 SVN_ERR(svn_sqlite__bindf(stmt, "iss",
12464 repos_id, repos_relpath, lock->token));
12466 if (lock->owner != NULL)
12467 SVN_ERR(svn_sqlite__bind_text(stmt, 4, lock->owner));
12469 if (lock->comment != NULL)
12470 SVN_ERR(svn_sqlite__bind_text(stmt, 5, lock->comment));
12472 if (lock->date != 0)
12473 SVN_ERR(svn_sqlite__bind_int64(stmt, 6, lock->date));
12475 SVN_ERR(svn_sqlite__insert(NULL, stmt));
12477 return SVN_NO_ERROR;
12482 svn_wc__db_lock_add(svn_wc__db_t *db,
12483 const char *local_abspath,
12484 const svn_wc__db_lock_t *lock,
12485 apr_pool_t *scratch_pool)
12487 svn_wc__db_wcroot_t *wcroot;
12488 const char *local_relpath;
12490 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12491 SVN_ERR_ASSERT(lock != NULL);
12493 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12494 local_abspath, scratch_pool, scratch_pool));
12495 VERIFY_USABLE_WCROOT(wcroot);
12497 SVN_WC__DB_WITH_TXN(
12498 lock_add_txn(wcroot, local_relpath, lock, scratch_pool),
12501 /* There may be some entries, and the lock info is now out of date. */
12502 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
12504 return SVN_NO_ERROR;
12508 /* The body of svn_wc__db_lock_remove().
12510 static svn_error_t *
12511 lock_remove_txn(svn_wc__db_wcroot_t *wcroot,
12512 const char *local_relpath,
12513 svn_skel_t *work_items,
12514 apr_pool_t *scratch_pool)
12516 const char *repos_relpath;
12517 apr_int64_t repos_id;
12518 svn_sqlite__stmt_t *stmt;
12520 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12521 &repos_relpath, &repos_id,
12522 NULL, NULL, NULL, NULL, NULL,
12523 NULL, NULL, NULL, NULL, NULL,
12524 wcroot, local_relpath,
12525 scratch_pool, scratch_pool));
12527 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12528 STMT_DELETE_LOCK));
12529 SVN_ERR(svn_sqlite__bindf(stmt, "is", repos_id, repos_relpath));
12531 SVN_ERR(svn_sqlite__step_done(stmt));
12533 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
12535 return SVN_NO_ERROR;
12540 svn_wc__db_lock_remove(svn_wc__db_t *db,
12541 const char *local_abspath,
12542 svn_skel_t *work_items,
12543 apr_pool_t *scratch_pool)
12545 svn_wc__db_wcroot_t *wcroot;
12546 const char *local_relpath;
12548 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12550 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12551 local_abspath, scratch_pool, scratch_pool));
12552 VERIFY_USABLE_WCROOT(wcroot);
12554 SVN_WC__DB_WITH_TXN(
12555 lock_remove_txn(wcroot, local_relpath, work_items, scratch_pool),
12558 /* There may be some entries, and the lock info is now out of date. */
12559 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
12561 return SVN_NO_ERROR;
12564 /* A helper for scan_addition().
12565 * Compute moved-from information for the node at LOCAL_RELPATH which
12566 * has been determined as having been moved-here.
12567 * If MOVED_FROM_RELPATH is not NULL, set *MOVED_FROM_RELPATH to the
12568 * path of the move-source node in *MOVED_FROM_RELPATH.
12569 * If DELETE_OP_ROOT_RELPATH is not NULL, set *DELETE_OP_ROOT_RELPATH
12570 * to the path of the op-root of the delete-half of the move.
12571 * If moved-from information cannot be derived, set both *MOVED_FROM_RELPATH
12572 * and *DELETE_OP_ROOT_RELPATH to NULL, and return a "copied" status.
12573 * COPY_OPT_ROOT_RELPATH is the relpath of the op-root of the copied-half
12575 static svn_error_t *
12576 get_moved_from_info(const char **moved_from_relpath,
12577 const char **moved_from_op_root_relpath,
12578 const char *moved_to_op_root_relpath,
12580 svn_wc__db_wcroot_t *wcroot,
12581 const char *local_relpath,
12582 apr_pool_t *result_pool,
12583 apr_pool_t *scratch_pool)
12585 svn_sqlite__stmt_t *stmt;
12586 svn_boolean_t have_row;
12588 /* Run a query to get the moved-from path from the DB. */
12589 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12590 STMT_SELECT_MOVED_FROM_RELPATH));
12591 SVN_ERR(svn_sqlite__bindf(stmt, "is",
12592 wcroot->wc_id, moved_to_op_root_relpath));
12593 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12597 /* The move was only recorded at the copy-half, possibly because
12598 * the move operation was interrupted mid-way between the copy
12599 * and the delete. Treat this node as a normal copy. */
12600 if (moved_from_relpath)
12601 *moved_from_relpath = NULL;
12602 if (moved_from_op_root_relpath)
12603 *moved_from_op_root_relpath = NULL;
12605 SVN_ERR(svn_sqlite__reset(stmt));
12606 return SVN_NO_ERROR;
12610 *op_depth = svn_sqlite__column_int(stmt, 1);
12612 if (moved_from_relpath || moved_from_op_root_relpath)
12614 const char *db_delete_op_root_relpath;
12616 /* The moved-from path from the DB is the relpath of
12617 * the op_root of the delete-half of the move. */
12618 db_delete_op_root_relpath = svn_sqlite__column_text(stmt, 0,
12620 if (moved_from_op_root_relpath)
12621 *moved_from_op_root_relpath = db_delete_op_root_relpath;
12623 if (moved_from_relpath)
12625 if (strcmp(moved_to_op_root_relpath, local_relpath) == 0)
12627 /* LOCAL_RELPATH is the op_root of the copied-half of the
12628 * move, so the correct MOVED_FROM_ABSPATH is the op-root
12629 * of the delete-half. */
12630 *moved_from_relpath = db_delete_op_root_relpath;
12634 const char *child_relpath;
12636 /* LOCAL_RELPATH is a child that was copied along with the
12637 * op_root of the copied-half of the move. Construct the
12638 * corresponding path beneath the op_root of the delete-half. */
12640 /* Grab the child path relative to the op_root of the move
12642 child_relpath = svn_relpath_skip_ancestor(
12643 moved_to_op_root_relpath, local_relpath);
12645 SVN_ERR_ASSERT(child_relpath && strlen(child_relpath) > 0);
12647 /* This join is valid because LOCAL_RELPATH has not been moved
12648 * within the copied-half of the move yet -- else, it would
12649 * be its own op_root. */
12650 *moved_from_relpath = svn_relpath_join(db_delete_op_root_relpath,
12657 SVN_ERR(svn_sqlite__reset(stmt));
12659 return SVN_NO_ERROR;
12662 /* Like svn_wc__db_scan_addition(), but with WCROOT+LOCAL_RELPATH instead of
12665 The output value of *ORIGINAL_REPOS_ID will be INVALID_REPOS_ID if there
12666 is no 'copy-from' repository. */
12667 static svn_error_t *
12668 scan_addition(svn_wc__db_status_t *status,
12669 const char **op_root_relpath_p,
12670 const char **repos_relpath,
12671 apr_int64_t *repos_id,
12672 const char **original_repos_relpath,
12673 apr_int64_t *original_repos_id,
12674 svn_revnum_t *original_revision,
12675 const char **moved_from_relpath,
12676 const char **moved_from_op_root_relpath,
12677 int *moved_from_op_depth,
12678 svn_wc__db_wcroot_t *wcroot,
12679 const char *local_relpath,
12680 apr_pool_t *result_pool,
12681 apr_pool_t *scratch_pool)
12683 const char *op_root_relpath;
12684 const char *build_relpath = "";
12686 /* Initialize most of the OUT parameters. Generally, we'll only be filling
12687 in a subset of these, so it is easier to init all up front. Note that
12688 the STATUS parameter will be initialized once we read the status of
12689 the specified node. */
12690 if (op_root_relpath_p)
12691 *op_root_relpath_p = NULL;
12692 if (original_repos_relpath)
12693 *original_repos_relpath = NULL;
12694 if (original_repos_id)
12695 *original_repos_id = INVALID_REPOS_ID;
12696 if (original_revision)
12697 *original_revision = SVN_INVALID_REVNUM;
12698 if (moved_from_relpath)
12699 *moved_from_relpath = NULL;
12700 if (moved_from_op_root_relpath)
12701 *moved_from_op_root_relpath = NULL;
12702 if (moved_from_op_depth)
12703 *moved_from_op_depth = 0;
12706 svn_sqlite__stmt_t *stmt;
12707 svn_boolean_t have_row;
12708 svn_wc__db_status_t presence;
12710 const char *repos_prefix_path;
12712 /* ### is it faster to fetch fewer columns? */
12713 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12714 STMT_SELECT_WORKING_NODE));
12715 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
12716 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12720 /* Reset statement before returning */
12721 SVN_ERR(svn_sqlite__reset(stmt));
12723 /* ### maybe we should return a usage error instead? */
12724 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12725 _("The node '%s' was not found."),
12726 path_for_error_message(wcroot,
12731 presence = svn_sqlite__column_token(stmt, 1, presence_map);
12733 /* The starting node should exist normally. */
12734 op_depth = svn_sqlite__column_int(stmt, 0);
12735 if (op_depth == 0 || (presence != svn_wc__db_status_normal
12736 && presence != svn_wc__db_status_incomplete))
12737 /* reset the statement as part of the error generation process */
12738 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
12739 svn_sqlite__reset(stmt),
12740 _("Expected node '%s' to be added."),
12741 path_for_error_message(wcroot,
12745 if (original_revision)
12746 *original_revision = svn_sqlite__column_revnum(stmt, 12);
12748 /* Provide the default status; we'll override as appropriate. */
12751 if (presence == svn_wc__db_status_normal)
12752 *status = svn_wc__db_status_added;
12754 *status = svn_wc__db_status_incomplete;
12758 /* Calculate the op root local path components */
12759 op_root_relpath = svn_relpath_prefix(local_relpath, op_depth,
12761 repos_prefix_path = svn_relpath_skip_ancestor(op_root_relpath,
12764 if (op_root_relpath_p)
12765 *op_root_relpath_p = apr_pstrdup(result_pool, op_root_relpath);
12767 /* ### This if-statement is quite redundant.
12768 * ### We're checking all these values again within the body anyway.
12769 * ### The body should be broken up appropriately and move into the
12770 * ### outer scope. */
12771 if (original_repos_relpath
12772 || original_repos_id
12773 || (original_revision
12774 && *original_revision == SVN_INVALID_REVNUM)
12776 || moved_from_relpath || moved_from_op_root_relpath)
12778 if (local_relpath != op_root_relpath)
12779 /* requery to get the add/copy root */
12781 SVN_ERR(svn_sqlite__reset(stmt));
12783 SVN_ERR(svn_sqlite__bindf(stmt, "is",
12784 wcroot->wc_id, op_root_relpath));
12785 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12789 /* Reset statement before returning */
12790 SVN_ERR(svn_sqlite__reset(stmt));
12792 /* ### maybe we should return a usage error instead? */
12793 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12794 _("The node '%s' was not found."),
12795 path_for_error_message(wcroot,
12800 if (original_revision
12801 && *original_revision == SVN_INVALID_REVNUM)
12802 *original_revision = svn_sqlite__column_revnum(stmt, 12);
12805 if (original_repos_relpath)
12806 *original_repos_relpath = svn_sqlite__column_text(stmt, 11,
12809 if (!svn_sqlite__column_is_null(stmt, 10)
12811 || original_repos_id
12812 || moved_from_relpath || moved_from_op_root_relpath))
12813 /* If column 10 (original_repos_id) is NULL,
12814 this is a plain add, not a copy or a move */
12816 svn_boolean_t moved_here;
12817 if (original_repos_id)
12818 *original_repos_id = svn_sqlite__column_int64(stmt, 10);
12820 moved_here = svn_sqlite__column_boolean(stmt, 13 /* moved_here */);
12822 *status = moved_here ? svn_wc__db_status_moved_here
12823 : svn_wc__db_status_copied;
12826 && (moved_from_relpath || moved_from_op_root_relpath))
12830 err = get_moved_from_info(moved_from_relpath,
12831 moved_from_op_root_relpath,
12833 moved_from_op_depth,
12834 wcroot, local_relpath,
12839 return svn_error_compose_create(
12840 err, svn_sqlite__reset(stmt));
12846 /* ### This loop here is to skip up to the first node which is a BASE node,
12847 because base_get_info() doesn't accommodate the scenario that
12848 we're looking at here; we found the true op_root, which may be inside
12849 further changed trees. */
12850 if (repos_relpath || repos_id)
12852 const char *base_relpath;
12858 SVN_ERR(svn_sqlite__reset(stmt));
12860 /* Pointing at op_depth, look at the parent */
12861 repos_prefix_path =
12862 svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
12865 op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
12868 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, op_root_relpath));
12869 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12874 op_depth = svn_sqlite__column_int(stmt, 0);
12876 /* Skip to op_depth */
12877 tmp = op_root_relpath;
12879 op_root_relpath = svn_relpath_prefix(op_root_relpath, op_depth,
12881 repos_prefix_path = svn_relpath_join(
12882 svn_relpath_skip_ancestor(op_root_relpath, tmp),
12883 repos_prefix_path, scratch_pool);
12886 SVN_ERR(svn_sqlite__reset(stmt));
12888 build_relpath = repos_prefix_path;
12890 /* If we're here, then we have an added/copied/moved (start) node, and
12891 CURRENT_ABSPATH now points to a BASE node. Figure out the repository
12892 information for the current node, and use that to compute the start
12893 node's repository information. */
12894 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12895 &base_relpath, repos_id,
12896 NULL, NULL, NULL, NULL, NULL,
12897 NULL, NULL, NULL, NULL, NULL,
12898 wcroot, op_root_relpath,
12899 scratch_pool, scratch_pool));
12902 *repos_relpath = svn_relpath_join(base_relpath, build_relpath,
12906 SVN_ERR(svn_sqlite__reset(stmt));
12908 /* Postconditions */
12912 SVN_ERR_ASSERT(*status == svn_wc__db_status_added
12913 || *status == svn_wc__db_status_copied
12914 || *status == svn_wc__db_status_incomplete
12915 || *status == svn_wc__db_status_moved_here);
12916 if (*status == svn_wc__db_status_added)
12918 SVN_ERR_ASSERT(!original_repos_relpath
12919 || *original_repos_relpath == NULL);
12920 SVN_ERR_ASSERT(!original_revision
12921 || *original_revision == SVN_INVALID_REVNUM);
12922 SVN_ERR_ASSERT(!original_repos_id
12923 || *original_repos_id == INVALID_REPOS_ID);
12925 /* An upgrade with a missing directory can leave INCOMPLETE working
12926 op-roots. See upgrade_tests.py 29: upgrade with missing replaced dir
12928 else if (*status != svn_wc__db_status_incomplete)
12930 SVN_ERR_ASSERT(!original_repos_relpath
12931 || *original_repos_relpath != NULL);
12932 SVN_ERR_ASSERT(!original_revision
12933 || *original_revision != SVN_INVALID_REVNUM);
12934 SVN_ERR_ASSERT(!original_repos_id
12935 || *original_repos_id != INVALID_REPOS_ID);
12938 SVN_ERR_ASSERT(!op_root_relpath_p || *op_root_relpath_p != NULL);
12941 return SVN_NO_ERROR;
12945 svn_wc__db_scan_addition_internal(
12946 svn_wc__db_status_t *status,
12947 const char **op_root_relpath_p,
12948 const char **repos_relpath,
12949 apr_int64_t *repos_id,
12950 const char **original_repos_relpath,
12951 apr_int64_t *original_repos_id,
12952 svn_revnum_t *original_revision,
12953 svn_wc__db_wcroot_t *wcroot,
12954 const char *local_relpath,
12955 apr_pool_t *result_pool,
12956 apr_pool_t *scratch_pool)
12958 return svn_error_trace(
12959 scan_addition(status, op_root_relpath_p, repos_relpath, repos_id,
12960 original_repos_relpath, original_repos_id,
12961 original_revision, NULL, NULL, NULL,
12962 wcroot, local_relpath, result_pool, scratch_pool));
12966 svn_wc__db_scan_addition(svn_wc__db_status_t *status,
12967 const char **op_root_abspath,
12968 const char **repos_relpath,
12969 const char **repos_root_url,
12970 const char **repos_uuid,
12971 const char **original_repos_relpath,
12972 const char **original_root_url,
12973 const char **original_uuid,
12974 svn_revnum_t *original_revision,
12976 const char *local_abspath,
12977 apr_pool_t *result_pool,
12978 apr_pool_t *scratch_pool)
12980 svn_wc__db_wcroot_t *wcroot;
12981 const char *local_relpath;
12982 const char *op_root_relpath = NULL;
12983 apr_int64_t repos_id = INVALID_REPOS_ID;
12984 apr_int64_t original_repos_id = INVALID_REPOS_ID;
12985 apr_int64_t *repos_id_p
12986 = (repos_root_url || repos_uuid) ? &repos_id : NULL;
12987 apr_int64_t *original_repos_id_p
12988 = (original_root_url || original_uuid) ? &original_repos_id : NULL;
12990 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12992 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12993 local_abspath, scratch_pool, scratch_pool));
12994 VERIFY_USABLE_WCROOT(wcroot);
12996 SVN_WC__DB_WITH_TXN4(
12997 scan_addition(status,
13001 repos_relpath, repos_id_p,
13002 original_repos_relpath, original_repos_id_p,
13005 wcroot, local_relpath, result_pool, scratch_pool),
13006 svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot,
13007 repos_id, result_pool),
13008 svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
13009 wcroot, original_repos_id,
13014 if (op_root_abspath)
13015 *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
13017 /* REPOS_ID must be valid if requested; ORIGINAL_REPOS_ID need not be. */
13018 SVN_ERR_ASSERT(repos_id_p == NULL || repos_id != INVALID_REPOS_ID);
13020 return SVN_NO_ERROR;
13024 svn_wc__db_scan_moved(const char **moved_from_abspath,
13025 const char **op_root_abspath,
13026 const char **op_root_moved_from_abspath,
13027 const char **moved_from_delete_abspath,
13029 const char *local_abspath,
13030 apr_pool_t *result_pool,
13031 apr_pool_t *scratch_pool)
13033 svn_wc__db_wcroot_t *wcroot;
13034 const char *local_relpath;
13035 svn_wc__db_status_t status;
13036 const char *op_root_relpath = NULL;
13037 const char *moved_from_relpath = NULL;
13038 const char *moved_from_op_root_relpath = NULL;
13039 int moved_from_op_depth = -1;
13041 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13043 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13044 local_abspath, scratch_pool, scratch_pool));
13045 VERIFY_USABLE_WCROOT(wcroot);
13047 SVN_WC__DB_WITH_TXN(
13048 scan_addition(&status,
13055 ? &moved_from_relpath
13057 (op_root_moved_from_abspath
13058 || moved_from_delete_abspath)
13059 ? &moved_from_op_root_relpath
13061 moved_from_delete_abspath
13062 ? &moved_from_op_depth
13064 wcroot, local_relpath, scratch_pool, scratch_pool),
13067 if (status != svn_wc__db_status_moved_here || !moved_from_relpath)
13068 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
13069 _("Path '%s' was not moved here"),
13070 path_for_error_message(wcroot, local_relpath,
13073 if (op_root_abspath)
13074 *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
13077 if (moved_from_abspath)
13078 *moved_from_abspath = svn_dirent_join(wcroot->abspath, moved_from_relpath,
13081 if (op_root_moved_from_abspath)
13082 *op_root_moved_from_abspath = svn_dirent_join(wcroot->abspath,
13083 moved_from_op_root_relpath,
13086 /* The deleted node is either where we moved from, or one of its ancestors */
13087 if (moved_from_delete_abspath)
13089 const char *tmp = svn_relpath_prefix(moved_from_op_root_relpath,
13090 moved_from_op_depth, scratch_pool);
13092 *moved_from_delete_abspath = svn_dirent_join(wcroot->abspath, tmp,
13096 return SVN_NO_ERROR;
13099 /* ### Recursive helper for svn_wc__db_follow_moved_to()
13101 static svn_error_t *
13102 follow_moved_to(svn_wc__db_wcroot_t *wcroot,
13103 const char *local_relpath,
13105 apr_array_header_t **moved_tos,
13106 apr_pool_t *result_pool,
13107 apr_pool_t *scratch_pool)
13109 svn_sqlite__stmt_t *stmt;
13110 svn_boolean_t have_row;
13111 int shadowing_op_depth;
13112 const char *ancestor_relpath;
13113 const char *node_moved_to = NULL;
13116 /* Obtain the depth of the node directly shadowing local_relpath
13117 as it exists at OP_DEPTH, and perhaps moved to info */
13118 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13119 STMT_SELECT_OP_DEPTH_MOVED_TO));
13120 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
13122 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13125 shadowing_op_depth = svn_sqlite__column_int(stmt, 0);
13126 node_moved_to = svn_sqlite__column_text(stmt, 1, result_pool);
13130 struct svn_wc__db_moved_to_t *moved_to;
13132 moved_to = apr_palloc(result_pool, sizeof(*moved_to));
13133 moved_to->op_depth = shadowing_op_depth;
13134 moved_to->local_relpath = node_moved_to;
13135 APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
13139 SVN_ERR(svn_sqlite__reset(stmt));
13143 /* Node is not shadowed, so not moved */
13144 return SVN_NO_ERROR;
13146 else if (node_moved_to)
13148 /* Moved directly, so we have the final location */
13149 return SVN_NO_ERROR;
13151 /* Need to handle being moved via an ancestor. */
13152 ancestor_relpath = local_relpath;
13153 for (i = relpath_depth(local_relpath); i > shadowing_op_depth; --i)
13155 const char *ancestor_moved_to;
13157 ancestor_relpath = svn_relpath_dirname(ancestor_relpath, scratch_pool);
13159 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13160 STMT_SELECT_MOVED_TO));
13161 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, ancestor_relpath,
13162 shadowing_op_depth));
13163 SVN_ERR(svn_sqlite__step_row(stmt));
13165 ancestor_moved_to = svn_sqlite__column_text(stmt, 0, scratch_pool);
13166 SVN_ERR(svn_sqlite__reset(stmt));
13167 if (ancestor_moved_to)
13169 struct svn_wc__db_moved_to_t *moved_to;
13172 = svn_relpath_join(ancestor_moved_to,
13173 svn_relpath_skip_ancestor(ancestor_relpath,
13177 moved_to = apr_palloc(result_pool, sizeof(*moved_to));
13178 moved_to->op_depth = shadowing_op_depth;
13179 moved_to->local_relpath = node_moved_to;
13180 APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
13182 SVN_ERR(follow_moved_to(wcroot, node_moved_to,
13183 relpath_depth(ancestor_moved_to),
13184 moved_tos, result_pool, scratch_pool));
13190 return SVN_NO_ERROR;
13194 svn_wc__db_follow_moved_to(apr_array_header_t **moved_tos,
13196 const char *local_abspath,
13197 apr_pool_t *result_pool,
13198 apr_pool_t *scratch_pool)
13200 svn_wc__db_wcroot_t *wcroot;
13201 const char *local_relpath;
13203 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13205 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13206 local_abspath, scratch_pool, scratch_pool));
13207 VERIFY_USABLE_WCROOT(wcroot);
13209 *moved_tos = apr_array_make(result_pool, 0,
13210 sizeof(struct svn_wc__db_moved_to_t *));
13212 /* ### Wrap in a transaction */
13213 SVN_WC__DB_WITH_TXN(follow_moved_to(wcroot, local_relpath, 0, moved_tos,
13214 result_pool, scratch_pool),
13217 /* ### Convert moved_to to abspath */
13219 return SVN_NO_ERROR;
13223 svn_wc__db_scan_moved_to_internal(const char **move_src_relpath,
13224 const char **move_dst_relpath,
13225 const char **delete_relpath,
13226 svn_wc__db_wcroot_t *wcroot,
13227 const char *local_relpath,
13229 apr_pool_t *result_pool,
13230 apr_pool_t *scratch_pool)
13232 svn_sqlite__stmt_t *stmt;
13233 svn_boolean_t have_row;
13234 int delete_op_depth;
13235 const char *relpath = local_relpath;
13236 const char *dst_relpath;
13238 SVN_ERR_ASSERT(local_relpath[0]); /* Not valid on the WC root */
13240 if (move_src_relpath)
13241 *move_src_relpath = NULL;
13242 if (move_dst_relpath)
13243 *move_dst_relpath = NULL;
13244 if (delete_relpath)
13245 *delete_relpath = NULL;
13247 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13248 STMT_SELECT_OP_DEPTH_MOVED_TO));
13249 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath, op_depth));
13251 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13255 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
13256 svn_sqlite__reset(stmt),
13257 _("Node '%s' is not shadowed"),
13258 path_for_error_message(wcroot, local_relpath,
13262 delete_op_depth = svn_sqlite__column_int(stmt, 0);
13263 dst_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
13265 SVN_ERR(svn_sqlite__reset(stmt));
13267 while (!dst_relpath && have_row)
13269 relpath = svn_relpath_dirname(relpath, scratch_pool);
13271 if (relpath_depth(relpath) < delete_op_depth)
13274 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13275 STMT_SELECT_DEPTH_NODE));
13276 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath,
13279 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13282 dst_relpath = svn_sqlite__column_text(stmt, 13, scratch_pool);
13284 SVN_ERR(svn_sqlite__reset(stmt));
13289 if (move_src_relpath)
13290 *move_src_relpath = apr_pstrdup(result_pool, relpath);
13292 if (move_dst_relpath)
13293 *move_dst_relpath = apr_pstrdup(result_pool, dst_relpath);
13295 if (delete_relpath)
13296 *delete_relpath = svn_relpath_prefix(local_relpath, delete_op_depth,
13300 return SVN_NO_ERROR;
13303 /* Public (within libsvn_wc) absolute path version of
13304 svn_wc__db_op_depth_moved_to with the op-depth hard-coded to
13307 svn_wc__db_base_moved_to(const char **move_dst_abspath,
13308 const char **move_dst_op_root_abspath,
13309 const char **move_src_root_abspath,
13310 const char **delete_abspath,
13312 const char *local_abspath,
13313 apr_pool_t *result_pool,
13314 apr_pool_t *scratch_pool)
13316 svn_wc__db_wcroot_t *wcroot;
13317 const char *local_relpath;
13318 const char *dst_root_relpath;
13319 const char *src_root_relpath, *delete_relpath;
13321 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13324 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13325 local_abspath, scratch_pool, scratch_pool));
13326 VERIFY_USABLE_WCROOT(wcroot);
13328 SVN_WC__DB_WITH_TXN(svn_wc__db_scan_moved_to_internal(&src_root_relpath,
13331 wcroot, local_relpath,
13337 if (move_dst_abspath)
13338 *move_dst_abspath =
13340 ? svn_dirent_join(wcroot->abspath,
13343 svn_relpath_skip_ancestor(src_root_relpath,
13349 if (move_dst_op_root_abspath)
13350 *move_dst_op_root_abspath =
13352 ? svn_dirent_join(wcroot->abspath, dst_root_relpath, result_pool)
13355 if (move_src_root_abspath)
13356 *move_src_root_abspath =
13358 ? svn_dirent_join(wcroot->abspath, src_root_relpath, result_pool)
13361 if (delete_abspath)
13364 ? svn_dirent_join(wcroot->abspath, delete_relpath, result_pool)
13367 return SVN_NO_ERROR;
13371 svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb,
13372 apr_int64_t *repos_id,
13373 apr_int64_t *wc_id,
13374 svn_wc__db_t *wc_db,
13375 const char *dir_abspath,
13376 const char *repos_root_url,
13377 const char *repos_uuid,
13378 apr_pool_t *scratch_pool)
13380 svn_wc__db_wcroot_t *wcroot;
13382 /* Upgrade is inherently exclusive so specify exclusive locking. */
13383 SVN_ERR(create_db(sdb, repos_id, wc_id, dir_abspath,
13384 repos_root_url, repos_uuid,
13386 NULL, SVN_INVALID_REVNUM, svn_depth_unknown,
13387 TRUE /* exclusive */,
13389 wc_db->state_pool, scratch_pool));
13391 SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
13392 apr_pstrdup(wc_db->state_pool,
13394 *sdb, *wc_id, FORMAT_FROM_SDB,
13395 FALSE /* auto-upgrade */,
13396 wc_db->state_pool, scratch_pool));
13398 /* The WCROOT is complete. Stash it into DB. */
13399 svn_hash_sets(wc_db->dir_data, wcroot->abspath, wcroot);
13401 return SVN_NO_ERROR;
13405 svn_wc__db_upgrade_insert_external(svn_wc__db_t *db,
13406 const char *local_abspath,
13407 svn_node_kind_t kind,
13408 const char *parent_abspath,
13409 const char *def_local_abspath,
13410 const char *repos_relpath,
13411 const char *repos_root_url,
13412 const char *repos_uuid,
13413 svn_revnum_t def_peg_revision,
13414 svn_revnum_t def_revision,
13415 apr_pool_t *scratch_pool)
13417 svn_wc__db_wcroot_t *wcroot;
13418 const char *def_local_relpath;
13419 svn_sqlite__stmt_t *stmt;
13420 svn_boolean_t have_row;
13421 apr_int64_t repos_id;
13423 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13425 /* We know only of DEF_LOCAL_ABSPATH that it definitely belongs to "this"
13426 * WC, i.e. where the svn:externals prop is set. The external target path
13427 * itself may be "hidden behind" other working copies. */
13428 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &def_local_relpath,
13429 db, def_local_abspath,
13430 scratch_pool, scratch_pool));
13431 VERIFY_USABLE_WCROOT(wcroot);
13434 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13435 STMT_SELECT_REPOSITORY));
13436 SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
13437 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13440 repos_id = svn_sqlite__column_int64(stmt, 0);
13441 SVN_ERR(svn_sqlite__reset(stmt));
13445 /* Need to set up a new repository row. */
13446 SVN_ERR(create_repos_id(&repos_id, repos_root_url, repos_uuid,
13447 wcroot->sdb, scratch_pool));
13450 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13451 STMT_INSERT_EXTERNAL));
13453 /* wc_id, local_relpath, parent_relpath, presence, kind, def_local_relpath,
13454 * repos_id, def_repos_relpath, def_operational_revision, def_revision */
13455 SVN_ERR(svn_sqlite__bindf(stmt, "issstsis",
13457 svn_dirent_skip_ancestor(wcroot->abspath,
13459 svn_dirent_skip_ancestor(wcroot->abspath,
13467 if (SVN_IS_VALID_REVNUM(def_peg_revision))
13468 SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, def_peg_revision));
13470 if (SVN_IS_VALID_REVNUM(def_revision))
13471 SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, def_revision));
13473 SVN_ERR(svn_sqlite__insert(NULL, stmt));
13475 return SVN_NO_ERROR;
13479 svn_wc__db_wq_add_internal(svn_wc__db_wcroot_t *wcroot,
13480 const svn_skel_t *work_item,
13481 apr_pool_t *scratch_pool)
13483 /* Add the work item(s) to the WORK_QUEUE. */
13484 return svn_error_trace(add_work_items(wcroot->sdb, work_item,
13489 svn_wc__db_wq_add(svn_wc__db_t *db,
13490 const char *wri_abspath,
13491 const svn_skel_t *work_item,
13492 apr_pool_t *scratch_pool)
13494 svn_wc__db_wcroot_t *wcroot;
13495 const char *local_relpath;
13497 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13499 /* Quick exit, if there are no work items to queue up. */
13500 if (work_item == NULL)
13501 return SVN_NO_ERROR;
13503 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13504 wri_abspath, scratch_pool, scratch_pool));
13505 VERIFY_USABLE_WCROOT(wcroot);
13507 /* Add the work item(s) to the WORK_QUEUE. */
13508 return svn_error_trace(add_work_items(wcroot->sdb, work_item,
13512 /* The body of svn_wc__db_wq_fetch_next().
13514 static svn_error_t *
13515 wq_fetch_next(apr_uint64_t *id,
13516 svn_skel_t **work_item,
13517 svn_wc__db_wcroot_t *wcroot,
13518 const char *local_relpath,
13519 apr_uint64_t completed_id,
13520 apr_pool_t *result_pool,
13521 apr_pool_t *scratch_pool)
13523 svn_sqlite__stmt_t *stmt;
13524 svn_boolean_t have_row;
13526 if (completed_id != 0)
13528 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13529 STMT_DELETE_WORK_ITEM));
13530 SVN_ERR(svn_sqlite__bind_int64(stmt, 1, completed_id));
13532 SVN_ERR(svn_sqlite__step_done(stmt));
13535 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13536 STMT_SELECT_WORK_ITEM));
13537 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13549 *id = svn_sqlite__column_int64(stmt, 0);
13551 val = svn_sqlite__column_blob(stmt, 1, &len, result_pool);
13553 *work_item = svn_skel__parse(val, len, result_pool);
13556 return svn_error_trace(svn_sqlite__reset(stmt));
13560 svn_wc__db_wq_fetch_next(apr_uint64_t *id,
13561 svn_skel_t **work_item,
13563 const char *wri_abspath,
13564 apr_uint64_t completed_id,
13565 apr_pool_t *result_pool,
13566 apr_pool_t *scratch_pool)
13568 svn_wc__db_wcroot_t *wcroot;
13569 const char *local_relpath;
13571 SVN_ERR_ASSERT(id != NULL);
13572 SVN_ERR_ASSERT(work_item != NULL);
13573 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13575 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13576 wri_abspath, scratch_pool, scratch_pool));
13577 VERIFY_USABLE_WCROOT(wcroot);
13579 SVN_WC__DB_WITH_TXN(
13580 wq_fetch_next(id, work_item,
13581 wcroot, local_relpath, completed_id,
13582 result_pool, scratch_pool),
13585 return SVN_NO_ERROR;
13588 /* Records timestamp and date for one or more files in wcroot */
13589 static svn_error_t *
13590 wq_record(svn_wc__db_wcroot_t *wcroot,
13591 apr_hash_t *record_map,
13592 apr_pool_t *scratch_pool)
13594 apr_hash_index_t *hi;
13595 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
13597 for (hi = apr_hash_first(scratch_pool, record_map); hi;
13598 hi = apr_hash_next(hi))
13600 const char *local_abspath = apr_hash_this_key(hi);
13601 const svn_io_dirent2_t *dirent = apr_hash_this_val(hi);
13602 const char *local_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
13605 svn_pool_clear(iterpool);
13607 if (! local_relpath)
13610 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
13611 dirent->filesize, dirent->mtime,
13615 svn_pool_destroy(iterpool);
13616 return SVN_NO_ERROR;
13620 svn_wc__db_wq_record_and_fetch_next(apr_uint64_t *id,
13621 svn_skel_t **work_item,
13623 const char *wri_abspath,
13624 apr_uint64_t completed_id,
13625 apr_hash_t *record_map,
13626 apr_pool_t *result_pool,
13627 apr_pool_t *scratch_pool)
13629 svn_wc__db_wcroot_t *wcroot;
13630 const char *local_relpath;
13632 SVN_ERR_ASSERT(id != NULL);
13633 SVN_ERR_ASSERT(work_item != NULL);
13634 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13636 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13637 wri_abspath, scratch_pool, scratch_pool));
13638 VERIFY_USABLE_WCROOT(wcroot);
13640 SVN_WC__DB_WITH_TXN(
13641 svn_error_compose_create(
13642 wq_fetch_next(id, work_item,
13643 wcroot, local_relpath, completed_id,
13644 result_pool, scratch_pool),
13645 wq_record(wcroot, record_map, scratch_pool)),
13648 return SVN_NO_ERROR;
13653 /* ### temporary API. remove before release. */
13655 svn_wc__db_temp_get_format(int *format,
13657 const char *local_dir_abspath,
13658 apr_pool_t *scratch_pool)
13660 svn_wc__db_wcroot_t *wcroot;
13661 const char *local_relpath;
13664 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13665 /* ### assert that we were passed a directory? */
13667 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13668 local_dir_abspath, scratch_pool, scratch_pool);
13670 /* If we hit an error examining this directory, then declare this
13671 directory to not be a working copy. */
13674 if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
13675 return svn_error_trace(err);
13676 svn_error_clear(err);
13678 /* Remap the returned error. */
13680 return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
13681 _("'%s' is not a working copy"),
13682 svn_dirent_local_style(local_dir_abspath,
13686 SVN_ERR_ASSERT(wcroot != NULL);
13687 SVN_ERR_ASSERT(wcroot->format >= 1);
13689 *format = wcroot->format;
13691 return SVN_NO_ERROR;
13694 /* ### temporary API. remove before release. */
13695 svn_wc_adm_access_t *
13696 svn_wc__db_temp_get_access(svn_wc__db_t *db,
13697 const char *local_dir_abspath,
13698 apr_pool_t *scratch_pool)
13700 const char *local_relpath;
13701 svn_wc__db_wcroot_t *wcroot;
13704 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13706 /* ### we really need to assert that we were passed a directory. sometimes
13707 ### adm_retrieve_internal is asked about a file, and then it asks us
13708 ### for an access baton for it. we should definitely return NULL, but
13709 ### ideally: the caller would never ask us about a non-directory. */
13711 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13712 db, local_dir_abspath, scratch_pool, scratch_pool);
13715 svn_error_clear(err);
13722 return svn_hash_gets(wcroot->access_cache, local_dir_abspath);
13726 /* ### temporary API. remove before release. */
13728 svn_wc__db_temp_set_access(svn_wc__db_t *db,
13729 const char *local_dir_abspath,
13730 svn_wc_adm_access_t *adm_access,
13731 apr_pool_t *scratch_pool)
13733 const char *local_relpath;
13734 svn_wc__db_wcroot_t *wcroot;
13737 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13738 /* ### assert that we were passed a directory? */
13740 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13741 db, local_dir_abspath, scratch_pool, scratch_pool);
13744 /* We don't even have a wcroot, so just bail. */
13745 svn_error_clear(err);
13749 /* Better not override something already there. */
13750 SVN_ERR_ASSERT_NO_RETURN(
13751 svn_hash_gets(wcroot->access_cache, local_dir_abspath) == NULL
13753 svn_hash_sets(wcroot->access_cache, local_dir_abspath, adm_access);
13757 /* ### temporary API. remove before release. */
13759 svn_wc__db_temp_close_access(svn_wc__db_t *db,
13760 const char *local_dir_abspath,
13761 svn_wc_adm_access_t *adm_access,
13762 apr_pool_t *scratch_pool)
13764 const char *local_relpath;
13765 svn_wc__db_wcroot_t *wcroot;
13767 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13768 /* ### assert that we were passed a directory? */
13770 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13771 local_dir_abspath, scratch_pool, scratch_pool));
13772 svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13774 return SVN_NO_ERROR;
13778 /* ### temporary API. remove before release. */
13780 svn_wc__db_temp_clear_access(svn_wc__db_t *db,
13781 const char *local_dir_abspath,
13782 apr_pool_t *scratch_pool)
13784 const char *local_relpath;
13785 svn_wc__db_wcroot_t *wcroot;
13788 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13789 /* ### assert that we were passed a directory? */
13791 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13792 db, local_dir_abspath, scratch_pool, scratch_pool);
13795 svn_error_clear(err);
13799 svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13804 svn_wc__db_temp_get_all_access(svn_wc__db_t *db,
13805 apr_pool_t *result_pool)
13807 apr_hash_t *result = apr_hash_make(result_pool);
13808 apr_hash_index_t *hi;
13810 for (hi = apr_hash_first(result_pool, db->dir_data);
13812 hi = apr_hash_next(hi))
13814 const svn_wc__db_wcroot_t *wcroot = apr_hash_this_val(hi);
13816 /* This is highly redundant, 'cause the same WCROOT will appear many
13817 times in dir_data. */
13818 result = apr_hash_overlay(result_pool, result, wcroot->access_cache);
13826 svn_wc__db_temp_borrow_sdb(svn_sqlite__db_t **sdb,
13828 const char *local_dir_abspath,
13829 apr_pool_t *scratch_pool)
13831 svn_wc__db_wcroot_t *wcroot;
13832 const char *local_relpath;
13834 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13836 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13837 local_dir_abspath, scratch_pool, scratch_pool));
13838 VERIFY_USABLE_WCROOT(wcroot);
13840 *sdb = wcroot->sdb;
13842 return SVN_NO_ERROR;
13847 svn_wc__db_read_conflict_victims(const apr_array_header_t **victims,
13849 const char *local_abspath,
13850 apr_pool_t *result_pool,
13851 apr_pool_t *scratch_pool)
13853 svn_wc__db_wcroot_t *wcroot;
13854 const char *local_relpath;
13855 svn_sqlite__stmt_t *stmt;
13856 svn_boolean_t have_row;
13857 apr_array_header_t *new_victims;
13859 /* The parent should be a working copy directory. */
13860 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13861 local_abspath, scratch_pool, scratch_pool));
13862 VERIFY_USABLE_WCROOT(wcroot);
13864 /* ### This will be much easier once we have all conflicts in one
13867 /* Look for text, tree and property conflicts in ACTUAL */
13868 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13869 STMT_SELECT_CONFLICT_VICTIMS));
13870 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13872 new_victims = apr_array_make(result_pool, 0, sizeof(const char *));
13874 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13877 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
13879 APR_ARRAY_PUSH(new_victims, const char *) =
13880 svn_relpath_basename(child_relpath, result_pool);
13882 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13885 SVN_ERR(svn_sqlite__reset(stmt));
13887 *victims = new_victims;
13888 return SVN_NO_ERROR;
13891 /* The body of svn_wc__db_get_conflict_marker_files().
13893 static svn_error_t *
13894 get_conflict_marker_files(apr_hash_t **marker_files_p,
13895 svn_wc__db_wcroot_t *wcroot,
13896 const char *local_relpath,
13898 apr_pool_t *result_pool,
13899 apr_pool_t *scratch_pool)
13901 svn_sqlite__stmt_t *stmt;
13902 svn_boolean_t have_row;
13903 apr_hash_t *marker_files = apr_hash_make(result_pool);
13905 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13906 STMT_SELECT_ACTUAL_NODE));
13907 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13908 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13910 if (have_row && !svn_sqlite__column_is_null(stmt, 2))
13913 const void *data = svn_sqlite__column_blob(stmt, 2, &len, NULL);
13914 svn_skel_t *conflicts;
13915 const apr_array_header_t *markers;
13918 conflicts = svn_skel__parse(data, len, scratch_pool);
13920 /* ### ADD markers to *marker_files */
13921 SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13923 result_pool, scratch_pool));
13925 for (i = 0; markers && (i < markers->nelts); i++)
13927 const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13929 svn_hash_sets(marker_files, marker_abspath, "");
13932 SVN_ERR(svn_sqlite__reset(stmt));
13934 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13935 STMT_SELECT_CONFLICT_VICTIMS));
13936 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13937 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13942 const void *data = svn_sqlite__column_blob(stmt, 1, &len, NULL);
13944 const apr_array_header_t *markers;
13949 svn_skel_t *conflicts;
13950 conflicts = svn_skel__parse(data, len, scratch_pool);
13952 SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13954 result_pool, scratch_pool));
13956 for (i = 0; markers && (i < markers->nelts); i++)
13958 const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13960 svn_hash_sets(marker_files, marker_abspath, "");
13964 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13967 if (apr_hash_count(marker_files))
13968 *marker_files_p = marker_files;
13970 *marker_files_p = NULL;
13972 return svn_error_trace(svn_sqlite__reset(stmt));
13976 svn_wc__db_get_conflict_marker_files(apr_hash_t **marker_files,
13978 const char *local_abspath,
13979 apr_pool_t *result_pool,
13980 apr_pool_t *scratch_pool)
13982 svn_wc__db_wcroot_t *wcroot;
13983 const char *local_relpath;
13985 /* The parent should be a working copy directory. */
13986 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13987 local_abspath, scratch_pool, scratch_pool));
13988 VERIFY_USABLE_WCROOT(wcroot);
13990 SVN_WC__DB_WITH_TXN(
13991 get_conflict_marker_files(marker_files, wcroot, local_relpath, db,
13992 result_pool, scratch_pool),
13995 return SVN_NO_ERROR;
14000 svn_wc__db_read_conflict(svn_skel_t **conflict,
14001 svn_node_kind_t *kind,
14002 apr_hash_t **props,
14004 const char *local_abspath,
14005 apr_pool_t *result_pool,
14006 apr_pool_t *scratch_pool)
14008 svn_wc__db_wcroot_t *wcroot;
14009 const char *local_relpath;
14011 /* The parent should be a working copy directory. */
14012 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14013 local_abspath, scratch_pool, scratch_pool));
14014 VERIFY_USABLE_WCROOT(wcroot);
14016 return svn_error_trace(svn_wc__db_read_conflict_internal(conflict, kind, props,
14017 wcroot, local_relpath,
14023 svn_wc__db_read_conflict_internal(svn_skel_t **conflict,
14024 svn_node_kind_t *kind,
14025 apr_hash_t **props,
14026 svn_wc__db_wcroot_t *wcroot,
14027 const char *local_relpath,
14028 apr_pool_t *result_pool,
14029 apr_pool_t *scratch_pool)
14031 svn_sqlite__stmt_t *stmt;
14032 svn_boolean_t have_row;
14035 *kind = svn_node_none;
14039 /* Check if we have a conflict in ACTUAL */
14040 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14041 STMT_SELECT_ACTUAL_NODE));
14042 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14044 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14048 apr_size_t cfl_len;
14049 const void *cfl_data;
14051 /* svn_skel__parse doesn't copy data, so store in result_pool */
14052 cfl_data = svn_sqlite__column_blob(stmt, 2, &cfl_len, result_pool);
14055 *conflict = svn_skel__parse(cfl_data, cfl_len, result_pool);
14063 err = svn_error_trace(svn_sqlite__column_properties(props, stmt, 1,
14068 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
14074 SVN_ERR(svn_sqlite__reset(stmt));
14076 if (!have_row || kind || (props && !*props))
14078 svn_error_t *err = NULL;
14079 svn_boolean_t have_info = FALSE;
14081 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14082 STMT_SELECT_NODE_INFO));
14084 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
14087 SVN_ERR(svn_sqlite__step(&have_info, stmt));
14093 svn_wc__db_status_t status;
14094 int op_depth = svn_sqlite__column_int(stmt, 0);
14096 status = svn_sqlite__column_token(stmt, 3, presence_map);
14099 err = convert_to_working_status(&status, status);
14101 if (!err && (status == svn_wc__db_status_normal
14102 || status == svn_wc__db_status_added
14103 || status == svn_wc__db_status_deleted
14104 || status == svn_wc__db_status_incomplete))
14106 *kind = svn_sqlite__column_token(stmt, 4, kind_map);
14110 /* Need props, and no props in ACTUAL? */
14111 if (!err && (props && !*props))
14113 err = svn_sqlite__column_properties(props, stmt, 14,
14114 result_pool, scratch_pool);
14118 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14120 if (!have_row && !have_info)
14122 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14123 _("The node '%s' was not found."),
14124 path_for_error_message(wcroot,
14130 return SVN_NO_ERROR;
14135 svn_wc__db_read_kind(svn_node_kind_t *kind,
14137 const char *local_abspath,
14138 svn_boolean_t allow_missing,
14139 svn_boolean_t show_deleted,
14140 svn_boolean_t show_hidden,
14141 apr_pool_t *scratch_pool)
14143 svn_wc__db_wcroot_t *wcroot;
14144 const char *local_relpath;
14145 svn_sqlite__stmt_t *stmt_info;
14146 svn_boolean_t have_info;
14147 svn_wc__db_status_t status;
14149 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14151 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14152 local_abspath, scratch_pool, scratch_pool));
14153 VERIFY_USABLE_WCROOT(wcroot);
14155 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
14156 STMT_SELECT_NODE_INFO));
14157 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
14158 SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
14164 *kind = svn_node_unknown;
14165 SVN_ERR(svn_sqlite__reset(stmt_info));
14166 return SVN_NO_ERROR;
14170 SVN_ERR(svn_sqlite__reset(stmt_info));
14171 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14172 _("The node '%s' was not found."),
14173 path_for_error_message(wcroot,
14179 status = svn_sqlite__column_token(stmt_info, 3, presence_map);
14181 if (show_deleted && status == svn_wc__db_status_base_deleted)
14183 /* Let's return the kind of what is really deleted insead of what
14184 we have cached in the base-deleted record */
14186 SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
14190 /* No lower layer deleted? Database inconsistency! */
14191 *kind = svn_node_none;
14192 return svn_error_trace(svn_sqlite__reset(stmt_info));
14196 if (!(show_deleted && show_hidden))
14198 int op_depth = svn_sqlite__column_int(stmt_info, 0);
14199 svn_boolean_t report_none = FALSE;
14202 SVN_ERR(convert_to_working_status(&status, status));
14206 case svn_wc__db_status_not_present:
14207 if (! (show_hidden && show_deleted))
14208 report_none = TRUE;
14210 case svn_wc__db_status_excluded:
14211 case svn_wc__db_status_server_excluded:
14213 report_none = TRUE;
14215 case svn_wc__db_status_deleted:
14216 if (! show_deleted)
14217 report_none = TRUE;
14225 *kind = svn_node_none;
14226 return svn_error_trace(svn_sqlite__reset(stmt_info));
14230 *kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
14232 return svn_error_trace(svn_sqlite__reset(stmt_info));
14236 svn_wc__db_is_wcroot(svn_boolean_t *is_wcroot,
14238 const char *local_abspath,
14239 apr_pool_t *scratch_pool)
14241 svn_wc__db_wcroot_t *wcroot;
14242 const char *local_relpath;
14244 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14246 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14247 local_abspath, scratch_pool, scratch_pool));
14248 VERIFY_USABLE_WCROOT(wcroot);
14250 if (*local_relpath != '\0')
14252 *is_wcroot = FALSE; /* Node is a file, or has a parent directory within
14254 return SVN_NO_ERROR;
14259 return SVN_NO_ERROR;
14262 /* Find a node's kind and whether it is switched, putting the outputs in
14263 * *IS_SWITCHED and *KIND. Either of the outputs may be NULL if not wanted.
14265 static svn_error_t *
14266 db_is_switched(svn_boolean_t *is_switched,
14267 svn_node_kind_t *kind,
14268 svn_wc__db_wcroot_t *wcroot,
14269 const char *local_relpath,
14270 apr_pool_t *scratch_pool)
14272 svn_wc__db_status_t status;
14273 apr_int64_t repos_id;
14274 const char *repos_relpath;
14276 const char *parent_local_relpath;
14277 apr_int64_t parent_repos_id;
14278 const char *parent_repos_relpath;
14280 SVN_ERR_ASSERT(*local_relpath != '\0'); /* Handled in wrapper */
14282 SVN_ERR(read_info(&status, kind, NULL, &repos_relpath, &repos_id, NULL,
14283 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
14284 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
14285 wcroot, local_relpath, scratch_pool, scratch_pool));
14287 if (status == svn_wc__db_status_server_excluded
14288 || status == svn_wc__db_status_excluded
14289 || status == svn_wc__db_status_not_present)
14291 return svn_error_createf(
14292 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14293 _("The node '%s' was not found."),
14294 path_for_error_message(wcroot, local_relpath,
14297 else if (! repos_relpath)
14299 /* Node is shadowed; easy out */
14301 *is_switched = FALSE;
14303 return SVN_NO_ERROR;
14307 return SVN_NO_ERROR;
14309 svn_relpath_split(&parent_local_relpath, &name, local_relpath, scratch_pool);
14311 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
14312 &parent_repos_relpath,
14313 &parent_repos_id, NULL, NULL, NULL,
14314 NULL, NULL, NULL, NULL, NULL,
14316 wcroot, parent_local_relpath,
14317 scratch_pool, scratch_pool));
14319 if (repos_id != parent_repos_id)
14320 *is_switched = TRUE;
14323 const char *expected_relpath;
14325 expected_relpath = svn_relpath_join(parent_repos_relpath, name,
14328 *is_switched = (strcmp(expected_relpath, repos_relpath) != 0);
14331 return SVN_NO_ERROR;
14335 svn_wc__db_is_switched(svn_boolean_t *is_wcroot,
14336 svn_boolean_t *is_switched,
14337 svn_node_kind_t *kind,
14339 const char *local_abspath,
14340 apr_pool_t *scratch_pool)
14342 svn_wc__db_wcroot_t *wcroot;
14343 const char *local_relpath;
14345 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14347 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14348 local_abspath, scratch_pool, scratch_pool));
14349 VERIFY_USABLE_WCROOT(wcroot);
14352 *is_switched = FALSE;
14354 if (*local_relpath == '\0')
14361 *kind = svn_node_dir;
14362 return SVN_NO_ERROR;
14366 *is_wcroot = FALSE;
14368 if (! is_switched && ! kind)
14369 return SVN_NO_ERROR;
14371 SVN_WC__DB_WITH_TXN(
14372 db_is_switched(is_switched, kind, wcroot, local_relpath, scratch_pool),
14374 return SVN_NO_ERROR;
14379 svn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath,
14381 const char *wri_abspath,
14382 apr_pool_t *result_pool,
14383 apr_pool_t *scratch_pool)
14385 svn_wc__db_wcroot_t *wcroot;
14386 const char *local_relpath;
14388 SVN_ERR_ASSERT(temp_dir_abspath != NULL);
14389 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
14391 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14392 wri_abspath, scratch_pool, scratch_pool));
14393 VERIFY_USABLE_WCROOT(wcroot);
14395 *temp_dir_abspath = svn_dirent_join_many(result_pool,
14397 svn_wc_get_adm_dir(scratch_pool),
14398 WCROOT_TEMPDIR_RELPATH,
14400 return SVN_NO_ERROR;
14404 /* Helper for wclock_obtain_cb() to steal an existing lock */
14405 static svn_error_t *
14406 wclock_steal(svn_wc__db_wcroot_t *wcroot,
14407 const char *local_relpath,
14408 apr_pool_t *scratch_pool)
14410 svn_sqlite__stmt_t *stmt;
14412 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_WC_LOCK));
14413 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14415 SVN_ERR(svn_sqlite__step_done(stmt));
14417 return SVN_NO_ERROR;
14421 /* The body of svn_wc__db_wclock_obtain().
14423 static svn_error_t *
14424 wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot,
14425 const char *local_relpath,
14426 int levels_to_lock,
14427 svn_boolean_t steal_lock,
14428 svn_boolean_t enforce_empty_wq,
14429 apr_pool_t *scratch_pool)
14431 svn_sqlite__stmt_t *stmt;
14433 const char *lock_relpath;
14436 svn_boolean_t got_row;
14438 svn_wc__db_wclock_t lock;
14440 /* Upgrade locks the root before the node exists. Apart from that
14441 the root node always exists so we will just skip the check.
14443 ### Perhaps the lock for upgrade should be created when the db is
14444 created? 1.6 used to lock .svn on creation. */
14445 if (local_relpath[0])
14447 svn_boolean_t exists;
14449 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
14451 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14452 _("The node '%s' was not found."),
14453 path_for_error_message(wcroot,
14458 if (enforce_empty_wq)
14459 SVN_ERR(svn_wc__db_verify_no_work(wcroot->sdb));
14461 /* Check if there are nodes locked below the new lock root */
14462 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_FIND_WC_LOCK));
14463 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14465 lock_depth = relpath_depth(local_relpath);
14466 max_depth = lock_depth + levels_to_lock;
14468 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14472 svn_boolean_t own_lock;
14474 lock_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
14476 /* If we are not locking with depth infinity, check if this lock
14477 voids our lock request */
14478 if (levels_to_lock >= 0
14479 && relpath_depth(lock_relpath) > max_depth)
14481 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14485 /* Check if we are the lock owner, because we should be able to
14486 extend our lock. */
14487 err = svn_wc__db_wclock_owns_lock_internal(&own_lock, wcroot,
14489 TRUE, scratch_pool);
14492 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14494 if (!own_lock && !steal_lock)
14496 SVN_ERR(svn_sqlite__reset(stmt));
14497 err = svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
14498 _("'%s' is already locked."),
14499 path_for_error_message(wcroot,
14502 return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14503 _("Working copy '%s' locked."),
14504 path_for_error_message(wcroot,
14508 else if (!own_lock)
14510 err = wclock_steal(wcroot, lock_relpath, scratch_pool);
14513 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14516 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14519 SVN_ERR(svn_sqlite__reset(stmt));
14522 SVN_ERR(wclock_steal(wcroot, local_relpath, scratch_pool));
14524 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_WC_LOCK));
14525 lock_relpath = local_relpath;
14529 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, lock_relpath));
14531 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14535 int levels = svn_sqlite__column_int(stmt, 0);
14537 levels += relpath_depth(lock_relpath);
14539 SVN_ERR(svn_sqlite__reset(stmt));
14541 if (levels == -1 || levels >= lock_depth)
14544 err = svn_error_createf(
14545 SVN_ERR_WC_LOCKED, NULL,
14546 _("'%s' is already locked."),
14547 svn_dirent_local_style(
14548 svn_dirent_join(wcroot->abspath,
14552 return svn_error_createf(
14553 SVN_ERR_WC_LOCKED, err,
14554 _("Working copy '%s' locked."),
14555 path_for_error_message(wcroot,
14560 break; /* There can't be interesting locks on higher nodes */
14563 SVN_ERR(svn_sqlite__reset(stmt));
14565 if (!*lock_relpath)
14568 lock_relpath = svn_relpath_dirname(lock_relpath, scratch_pool);
14571 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_WC_LOCK));
14572 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14574 err = svn_sqlite__insert(NULL, stmt);
14576 return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14577 _("Failed to lock working copy '%s'."),
14578 path_for_error_message(wcroot,
14582 /* And finally store that we obtained the lock */
14583 lock.local_relpath = apr_pstrdup(wcroot->owned_locks->pool, local_relpath);
14584 lock.levels = levels_to_lock;
14585 APR_ARRAY_PUSH(wcroot->owned_locks, svn_wc__db_wclock_t) = lock;
14587 return SVN_NO_ERROR;
14592 svn_wc__db_wclock_obtain(svn_wc__db_t *db,
14593 const char *local_abspath,
14594 int levels_to_lock,
14595 svn_boolean_t steal_lock,
14596 apr_pool_t *scratch_pool)
14598 svn_wc__db_wcroot_t *wcroot;
14599 const char *local_relpath;
14601 SVN_ERR_ASSERT(levels_to_lock >= -1);
14602 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14604 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14606 scratch_pool, scratch_pool));
14607 VERIFY_USABLE_WCROOT(wcroot);
14612 int depth = relpath_depth(local_relpath);
14614 for (i = 0; i < wcroot->owned_locks->nelts; i++)
14616 svn_wc__db_wclock_t* lock = &APR_ARRAY_IDX(wcroot->owned_locks,
14617 i, svn_wc__db_wclock_t);
14619 if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14620 && (lock->levels == -1
14621 || (lock->levels + relpath_depth(lock->local_relpath))
14624 return svn_error_createf(
14625 SVN_ERR_WC_LOCKED, NULL,
14626 _("'%s' is already locked via '%s'."),
14627 svn_dirent_local_style(local_abspath, scratch_pool),
14628 path_for_error_message(wcroot, lock->local_relpath,
14634 SVN_WC__DB_WITH_TXN(
14635 wclock_obtain_cb(wcroot, local_relpath, levels_to_lock, steal_lock,
14636 db->enforce_empty_wq, scratch_pool),
14638 return SVN_NO_ERROR;
14642 /* The body of svn_wc__db_wclock_find_root() and svn_wc__db_wclocked(). */
14643 static svn_error_t *
14644 find_wclock(const char **lock_relpath,
14645 svn_wc__db_wcroot_t *wcroot,
14646 const char *dir_relpath,
14647 apr_pool_t *result_pool,
14648 apr_pool_t *scratch_pool)
14650 svn_sqlite__stmt_t *stmt;
14651 svn_boolean_t have_row;
14652 int dir_depth = relpath_depth(dir_relpath);
14653 const char *first_relpath;
14655 /* Check for locks on all directories that might be ancestors.
14656 As our new apis only use recursive locks the number of locks stored
14657 in the DB will be very low */
14658 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14659 STMT_SELECT_ANCESTOR_WCLOCKS));
14661 /* Get the top level relpath to reduce the worst case number of results
14662 to the number of directories below this node plus two.
14663 (1: the node itself and 2: the wcroot). */
14664 first_relpath = strchr(dir_relpath, '/');
14666 if (first_relpath != NULL)
14667 first_relpath = apr_pstrndup(scratch_pool, dir_relpath,
14668 first_relpath - dir_relpath);
14670 first_relpath = dir_relpath;
14672 SVN_ERR(svn_sqlite__bindf(stmt, "iss",
14677 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14681 const char *relpath = svn_sqlite__column_text(stmt, 0, NULL);
14683 if (svn_relpath_skip_ancestor(relpath, dir_relpath))
14685 int locked_levels = svn_sqlite__column_int(stmt, 1);
14686 int row_depth = relpath_depth(relpath);
14688 if (locked_levels == -1
14689 || locked_levels + row_depth >= dir_depth)
14691 *lock_relpath = apr_pstrdup(result_pool, relpath);
14692 SVN_ERR(svn_sqlite__reset(stmt));
14693 return SVN_NO_ERROR;
14697 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14700 *lock_relpath = NULL;
14702 return svn_error_trace(svn_sqlite__reset(stmt));
14705 static svn_error_t *
14706 is_wclocked(svn_boolean_t *locked,
14707 svn_wc__db_wcroot_t *wcroot,
14708 const char *dir_relpath,
14709 apr_pool_t *scratch_pool)
14711 const char *lock_relpath;
14713 SVN_ERR(find_wclock(&lock_relpath, wcroot, dir_relpath,
14714 scratch_pool, scratch_pool));
14715 *locked = (lock_relpath != NULL);
14716 return SVN_NO_ERROR;
14721 svn_wc__db_wclock_find_root(const char **lock_abspath,
14723 const char *local_abspath,
14724 apr_pool_t *result_pool,
14725 apr_pool_t *scratch_pool)
14727 svn_wc__db_wcroot_t *wcroot;
14728 const char *local_relpath;
14729 const char *lock_relpath;
14731 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14732 local_abspath, scratch_pool, scratch_pool));
14733 VERIFY_USABLE_WCROOT(wcroot);
14735 SVN_WC__DB_WITH_TXN(
14736 find_wclock(&lock_relpath, wcroot, local_relpath,
14737 scratch_pool, scratch_pool),
14741 *lock_abspath = NULL;
14743 SVN_ERR(svn_wc__db_from_relpath(lock_abspath, db, wcroot->abspath,
14744 lock_relpath, result_pool, scratch_pool));
14745 return SVN_NO_ERROR;
14750 svn_wc__db_wclocked(svn_boolean_t *locked,
14752 const char *local_abspath,
14753 apr_pool_t *scratch_pool)
14755 svn_wc__db_wcroot_t *wcroot;
14756 const char *local_relpath;
14758 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14759 local_abspath, scratch_pool, scratch_pool));
14760 VERIFY_USABLE_WCROOT(wcroot);
14762 SVN_WC__DB_WITH_TXN(
14763 is_wclocked(locked, wcroot, local_relpath, scratch_pool),
14766 return SVN_NO_ERROR;
14771 svn_wc__db_wclock_release(svn_wc__db_t *db,
14772 const char *local_abspath,
14773 apr_pool_t *scratch_pool)
14775 svn_sqlite__stmt_t *stmt;
14776 svn_wc__db_wcroot_t *wcroot;
14777 const char *local_relpath;
14779 apr_array_header_t *owned_locks;
14781 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14782 local_abspath, scratch_pool, scratch_pool));
14784 VERIFY_USABLE_WCROOT(wcroot);
14786 /* First check and remove the owns-lock information as failure in
14787 removing the db record implies that we have to steal the lock later. */
14788 owned_locks = wcroot->owned_locks;
14789 for (i = 0; i < owned_locks->nelts; i++)
14791 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14792 svn_wc__db_wclock_t);
14794 if (strcmp(lock->local_relpath, local_relpath) == 0)
14798 if (i >= owned_locks->nelts)
14799 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
14800 _("Working copy not locked at '%s'."),
14801 svn_dirent_local_style(local_abspath,
14804 if (i < owned_locks->nelts)
14806 owned_locks->nelts--;
14808 /* Move the last item in the array to the deleted place */
14809 if (owned_locks->nelts > 0)
14810 APR_ARRAY_IDX(owned_locks, i, svn_wc__db_wclock_t) =
14811 APR_ARRAY_IDX(owned_locks, owned_locks->nelts, svn_wc__db_wclock_t);
14814 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14815 STMT_DELETE_WC_LOCK));
14817 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14819 SVN_ERR(svn_sqlite__step_done(stmt));
14821 return SVN_NO_ERROR;
14825 /* Like svn_wc__db_wclock_owns_lock() but taking WCROOT+LOCAL_RELPATH instead
14826 of DB+LOCAL_ABSPATH. */
14828 svn_wc__db_wclock_owns_lock_internal(svn_boolean_t *own_lock,
14829 svn_wc__db_wcroot_t *wcroot,
14830 const char *local_relpath,
14831 svn_boolean_t exact,
14832 apr_pool_t *scratch_pool)
14834 apr_array_header_t *owned_locks;
14839 owned_locks = wcroot->owned_locks;
14840 lock_level = relpath_depth(local_relpath);
14844 for (i = 0; i < owned_locks->nelts; i++)
14846 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14847 svn_wc__db_wclock_t);
14849 if (strcmp(lock->local_relpath, local_relpath) == 0)
14852 return SVN_NO_ERROR;
14858 for (i = 0; i < owned_locks->nelts; i++)
14860 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14861 svn_wc__db_wclock_t);
14863 if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14864 && (lock->levels == -1
14865 || ((relpath_depth(lock->local_relpath) + lock->levels)
14869 return SVN_NO_ERROR;
14874 return SVN_NO_ERROR;
14879 svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
14881 const char *local_abspath,
14882 svn_boolean_t exact,
14883 apr_pool_t *scratch_pool)
14885 svn_wc__db_wcroot_t *wcroot;
14886 const char *local_relpath;
14888 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14889 local_abspath, scratch_pool, scratch_pool));
14892 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
14893 _("The node '%s' was not found."),
14894 svn_dirent_local_style(local_abspath,
14897 VERIFY_USABLE_WCROOT(wcroot);
14899 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(own_lock, wcroot, local_relpath,
14900 exact, scratch_pool));
14902 return SVN_NO_ERROR;
14905 /* The body of svn_wc__db_temp_op_end_directory_update().
14907 static svn_error_t *
14908 end_directory_update(svn_wc__db_wcroot_t *wcroot,
14909 const char *local_relpath,
14910 apr_pool_t *scratch_pool)
14912 svn_sqlite__stmt_t *stmt;
14913 svn_wc__db_status_t base_status;
14915 SVN_ERR(svn_wc__db_base_get_info_internal(&base_status, NULL, NULL, NULL,
14916 NULL, NULL, NULL, NULL, NULL,
14917 NULL, NULL, NULL, NULL, NULL, NULL,
14918 wcroot, local_relpath,
14919 scratch_pool, scratch_pool));
14921 if (base_status == svn_wc__db_status_normal)
14922 return SVN_NO_ERROR;
14924 SVN_ERR_ASSERT(base_status == svn_wc__db_status_incomplete);
14926 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14927 STMT_UPDATE_NODE_BASE_PRESENCE));
14928 SVN_ERR(svn_sqlite__bindf(stmt, "ist", wcroot->wc_id, local_relpath,
14929 presence_map, svn_wc__db_status_normal));
14930 SVN_ERR(svn_sqlite__step_done(stmt));
14932 return SVN_NO_ERROR;
14936 svn_wc__db_temp_op_end_directory_update(svn_wc__db_t *db,
14937 const char *local_dir_abspath,
14938 apr_pool_t *scratch_pool)
14940 svn_wc__db_wcroot_t *wcroot;
14941 const char *local_relpath;
14943 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
14945 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14946 local_dir_abspath, scratch_pool, scratch_pool));
14947 VERIFY_USABLE_WCROOT(wcroot);
14949 SVN_WC__DB_WITH_TXN(
14950 end_directory_update(wcroot, local_relpath, scratch_pool),
14953 SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_empty,
14956 return SVN_NO_ERROR;
14960 /* The body of svn_wc__db_temp_op_start_directory_update().
14962 static svn_error_t *
14963 start_directory_update_txn(svn_wc__db_wcroot_t *wcroot,
14964 const char *local_relpath,
14965 const char *new_repos_relpath,
14966 svn_revnum_t new_rev,
14967 apr_pool_t *scratch_pool)
14969 svn_sqlite__stmt_t *stmt;
14971 /* Note: In the majority of calls, the repos_relpath is unchanged. */
14972 /* ### TODO: Maybe check if we can make repos_relpath NULL. */
14973 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14974 STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH));
14976 SVN_ERR(svn_sqlite__bindf(stmt, "istrs",
14979 presence_map, svn_wc__db_status_incomplete,
14981 new_repos_relpath));
14982 SVN_ERR(svn_sqlite__step_done(stmt));
14984 return SVN_NO_ERROR;
14989 svn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db,
14990 const char *local_abspath,
14991 const char *new_repos_relpath,
14992 svn_revnum_t new_rev,
14993 apr_pool_t *scratch_pool)
14995 svn_wc__db_wcroot_t *wcroot;
14996 const char *local_relpath;
14998 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14999 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_rev));
15000 SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
15002 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15003 local_abspath, scratch_pool, scratch_pool));
15004 VERIFY_USABLE_WCROOT(wcroot);
15006 SVN_WC__DB_WITH_TXN(
15007 start_directory_update_txn(wcroot, local_relpath,
15008 new_repos_relpath, new_rev, scratch_pool),
15011 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
15013 return SVN_NO_ERROR;
15016 /* Helper for svn_wc__db_op_make_copy_internal */
15017 static svn_error_t *
15018 db_move_moved_to(svn_wc__db_wcroot_t *wcroot,
15019 const char *src1_relpath,
15021 const char *src2_relpath,
15023 const char *dst_relpath,
15024 apr_pool_t *scratch_pool)
15026 svn_sqlite__stmt_t *stmt;
15029 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15030 STMT_UPDATE_MOVED_TO_RELPATH));
15031 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
15032 src1_relpath, src1_op_depth));
15033 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
15035 if (affected_rows == 1)
15037 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15038 STMT_UPDATE_MOVED_TO_RELPATH));
15039 SVN_ERR(svn_sqlite__bindf(stmt, "isds", wcroot->wc_id,
15040 src2_relpath, src2_op_depth,
15042 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
15044 if (affected_rows != 1)
15045 return svn_error_create(SVN_ERR_WC_PATH_NOT_FOUND, NULL, NULL);
15047 return SVN_NO_ERROR;
15050 static svn_error_t *
15051 db_move_moved_to_down_recursive(svn_wc__db_wcroot_t *wcroot,
15052 const char *local_relpath,
15053 int new_shadow_layer,
15054 apr_pool_t *scratch_pool)
15056 svn_sqlite__stmt_t *stmt;
15057 svn_boolean_t have_row;
15058 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15060 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15061 STMT_SELECT_MOVED_DESCENDANTS_SRC));
15062 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15063 new_shadow_layer));
15064 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15069 const char *src_relpath;
15070 const char *dst_relpath;
15073 svn_pool_clear(iterpool);
15075 del_op_depth = svn_sqlite__column_int(stmt, 0);
15076 src_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
15077 dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool);
15079 err = svn_error_trace(
15082 src_relpath, del_op_depth,
15083 src_relpath, new_shadow_layer,
15084 dst_relpath, iterpool));
15087 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
15089 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15092 SVN_ERR(svn_sqlite__reset(stmt));
15094 return SVN_NO_ERROR;
15098 /* The body of svn_wc__db_temp_op_make_copy(). This is
15099 used by the update editor when deleting a base node tree would be a
15100 tree-conflict because there are changes to subtrees. This function
15101 inserts a copy of the base node tree below any existing working
15102 subtrees. Given a tree:
15107 A/B normal - normal
15108 A/B/C normal - base-del normal
15109 A/F normal - normal
15110 A/F/G normal - normal
15111 A/F/H normal - base-deleted normal
15112 A/F/E normal - not-present
15116 This function adds layers to A and some of its descendants in an attempt
15117 to make the working copy look like as if it were a copy of the BASE nodes.
15122 A/B normal norm norm
15123 A/B/C normal norm base-del normal
15124 A/F normal norm norm
15125 A/F/G normal norm norm
15126 A/F/H normal norm not-pres
15127 A/F/E normal norm base-del
15129 A/X/Y incomplete incomplete
15131 static svn_error_t *
15132 make_copy_txn(svn_wc__db_wcroot_t *wcroot,
15133 const char *local_relpath,
15134 apr_int64_t last_repos_id,
15135 const char *last_repos_relpath,
15136 svn_revnum_t last_revision,
15138 svn_boolean_t shadowed,
15139 int root_shadow_depth,
15140 apr_pool_t *scratch_pool)
15142 svn_sqlite__stmt_t *stmt;
15143 svn_boolean_t have_row = FALSE;
15144 svn_revnum_t revision;
15145 apr_int64_t repos_id;
15146 const char *repos_relpath;
15147 svn_node_kind_t kind;
15148 int op_depth = relpath_depth(local_relpath);
15150 if (last_op_depth != op_depth)
15152 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15153 STMT_SELECT_DEPTH_NODE));
15154 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15156 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15157 SVN_ERR(svn_sqlite__reset(stmt));
15162 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &kind, &revision,
15163 &repos_relpath, &repos_id, NULL,
15164 NULL, NULL, NULL, NULL, NULL, NULL,
15166 wcroot, local_relpath,
15167 scratch_pool, scratch_pool));
15169 if (last_repos_relpath
15170 && repos_id == last_repos_id
15171 && revision == last_revision)
15173 const char *name = svn_relpath_skip_ancestor(last_repos_relpath,
15176 if (name && strcmp(name, svn_relpath_basename(local_relpath, NULL)) == 0)
15177 op_depth = last_op_depth;
15180 /* Can we add a new copy node at the wanted op-depth? */
15181 if (!have_row || op_depth == last_op_depth)
15185 SVN_ERR(svn_sqlite__get_statement(
15186 &stmt, wcroot->sdb,
15187 STMT_INSERT_WORKING_NODE_FROM_BASE_COPY));
15188 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15190 SVN_ERR(svn_sqlite__step_done(stmt));
15193 SVN_ERR(db_extend_parent_delete(wcroot, local_relpath, kind,
15194 op_depth, scratch_pool));
15196 if (kind == svn_node_dir)
15198 const apr_array_header_t *children;
15199 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15201 SVN_ERR(gather_children(&children, wcroot, local_relpath,
15202 STMT_SELECT_OP_DEPTH_CHILDREN, 0,
15203 scratch_pool, iterpool));
15205 for (i = 0; i < children->nelts; i++)
15207 const char *name = APR_ARRAY_IDX(children, i, const char *);
15208 const char *copy_relpath;
15210 svn_pool_clear(iterpool);
15212 copy_relpath = svn_relpath_join(local_relpath, name, iterpool);
15214 SVN_ERR(make_copy_txn(wcroot, copy_relpath,
15215 repos_id, repos_relpath, revision,
15216 op_depth, shadowed, root_shadow_depth,
15219 svn_pool_destroy(iterpool);
15224 /* Auch... we can't make a copy of whatever comes deeper, as this
15225 op-depth is already filled by something else. Let's hope
15226 the user doesn't mind.
15228 Luckily we know that the moves are already moved to the shadowing
15229 layer, so we can just remove dangling base-deletes if there are
15232 /* BASE_DELETED may be at op_depth, so let's use last_op_depth! */
15233 SVN_ERR(db_move_moved_to_down_recursive(wcroot, local_relpath,
15237 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15238 STMT_DELETE_WORKING_BASE_DELETE));
15239 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15241 SVN_ERR(svn_sqlite__step_done(stmt));
15242 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15243 STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE));
15244 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15246 SVN_ERR(svn_sqlite__step_done(stmt));
15249 /* Insert a not-present node to mark that we don't know what exists here.
15251 We do this last (after recursing), to allow the move fix-up code to
15252 see the original moves. */
15253 if (last_op_depth > 0 && last_op_depth != op_depth)
15255 insert_working_baton_t iwb;
15258 iwb.presence = svn_wc__db_status_not_present;
15259 iwb.op_depth = last_op_depth;
15261 iwb.original_repos_id = repos_id;
15262 iwb.original_repos_relpath = repos_relpath;
15263 iwb.original_revnum = revision;
15266 SVN_ERR(insert_working_node(&iwb, wcroot, local_relpath, scratch_pool));
15269 return SVN_NO_ERROR;
15274 svn_wc__db_op_make_copy_internal(svn_wc__db_wcroot_t *wcroot,
15275 const char *local_relpath,
15276 svn_boolean_t move_move_info,
15277 const svn_skel_t *conflicts,
15278 const svn_skel_t *work_items,
15279 apr_pool_t *scratch_pool)
15281 svn_sqlite__stmt_t *stmt;
15282 svn_boolean_t have_row;
15285 /* The update editor is supposed to call this function when there is
15286 no working node for LOCAL_ABSPATH. */
15287 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15288 STMT_SELECT_WORKING_NODE));
15289 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15290 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15292 op_depth = svn_sqlite__column_int(stmt, 0);
15293 SVN_ERR(svn_sqlite__reset(stmt));
15297 if (op_depth == relpath_depth(local_relpath))
15298 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
15299 _("Modification of '%s' already exists"),
15300 path_for_error_message(wcroot,
15304 /* We have a working layer, but not one at the op-depth of local-relpath,
15305 so we can create a copy by just copying the lower layer */
15307 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15308 STMT_COPY_OP_DEPTH_RECURSIVE));
15309 SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath,
15310 op_depth, relpath_depth(local_relpath)));
15311 SVN_ERR(svn_sqlite__step_done(stmt));
15317 op_depth = relpath_depth(local_relpath);
15318 /* We don't allow copies to contain server-excluded nodes;
15319 the update editor is going to have to bail out. */
15320 SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath,
15323 /* Insert a shadowing layer */
15324 SVN_ERR(svn_sqlite__get_statement(
15325 &stmt, wcroot->sdb,
15326 STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
15328 /* As we are keeping whatever is below, move the*/
15330 SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
15331 wcroot->wc_id, local_relpath,
15333 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
15334 SVN_ERR_ASSERT(affected_rows > 0);
15336 if (!move_move_info)
15337 SVN_ERR(db_move_moved_to_down_recursive(wcroot, local_relpath,
15338 op_depth, scratch_pool));
15341 SVN_ERR(make_copy_txn(wcroot, local_relpath,
15342 INVALID_REPOS_ID, NULL, SVN_INVALID_REVNUM,
15343 op_depth, FALSE, op_depth,
15348 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
15349 conflicts, scratch_pool));
15351 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
15353 return SVN_NO_ERROR;
15358 svn_wc__db_op_make_copy(svn_wc__db_t *db,
15359 const char *local_abspath,
15360 const svn_skel_t *conflicts,
15361 const svn_skel_t *work_items,
15362 apr_pool_t *scratch_pool)
15364 svn_wc__db_wcroot_t *wcroot;
15365 const char *local_relpath;
15367 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15369 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15370 local_abspath, scratch_pool, scratch_pool));
15371 VERIFY_USABLE_WCROOT(wcroot);
15373 SVN_WC__DB_WITH_TXN(
15374 svn_wc__db_op_make_copy_internal(wcroot, local_relpath, FALSE,
15375 conflicts, work_items,
15379 SVN_ERR(flush_entries(wcroot, local_abspath,
15380 svn_depth_infinity, scratch_pool));
15382 return SVN_NO_ERROR;
15386 svn_wc__db_info_below_working(svn_boolean_t *have_base,
15387 svn_boolean_t *have_work,
15388 svn_wc__db_status_t *status,
15390 const char *local_abspath,
15391 apr_pool_t *scratch_pool)
15393 svn_wc__db_wcroot_t *wcroot;
15394 const char *local_relpath;
15396 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15398 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15399 local_abspath, scratch_pool, scratch_pool));
15400 VERIFY_USABLE_WCROOT(wcroot);
15401 SVN_ERR(info_below_working(have_base, have_work, status,
15402 wcroot, local_relpath, -1, scratch_pool));
15404 return SVN_NO_ERROR;
15408 svn_wc__db_get_not_present_descendants(const apr_array_header_t **descendants,
15410 const char *local_abspath,
15411 apr_pool_t *result_pool,
15412 apr_pool_t *scratch_pool)
15414 svn_wc__db_wcroot_t *wcroot;
15415 const char *local_relpath;
15416 svn_sqlite__stmt_t *stmt;
15417 svn_boolean_t have_row;
15419 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15421 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15422 local_abspath, scratch_pool, scratch_pool));
15423 VERIFY_USABLE_WCROOT(wcroot);
15425 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15426 STMT_SELECT_NOT_PRESENT_DESCENDANTS));
15428 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
15431 relpath_depth(local_relpath)));
15433 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15437 apr_array_header_t *paths;
15439 paths = apr_array_make(result_pool, 4, sizeof(const char*));
15442 const char *found_relpath = svn_sqlite__column_text(stmt, 0, NULL);
15444 APR_ARRAY_PUSH(paths, const char *)
15445 = apr_pstrdup(result_pool, svn_relpath_skip_ancestor(
15446 local_relpath, found_relpath));
15448 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15451 *descendants = paths;
15454 *descendants = apr_array_make(result_pool, 0, sizeof(const char*));
15456 return svn_error_trace(svn_sqlite__reset(stmt));
15460 /* Like svn_wc__db_min_max_revisions(),
15461 * but accepts a WCROOT/LOCAL_RELPATH pair. */
15462 static svn_error_t *
15463 get_min_max_revisions(svn_revnum_t *min_revision,
15464 svn_revnum_t *max_revision,
15465 svn_wc__db_wcroot_t *wcroot,
15466 const char *local_relpath,
15467 svn_boolean_t committed,
15468 apr_pool_t *scratch_pool)
15470 svn_sqlite__stmt_t *stmt;
15471 svn_revnum_t min_rev, max_rev;
15473 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15474 STMT_SELECT_MIN_MAX_REVISIONS));
15475 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15476 SVN_ERR(svn_sqlite__step_row(stmt));
15480 min_rev = svn_sqlite__column_revnum(stmt, 2);
15481 max_rev = svn_sqlite__column_revnum(stmt, 3);
15485 min_rev = svn_sqlite__column_revnum(stmt, 0);
15486 max_rev = svn_sqlite__column_revnum(stmt, 1);
15489 /* The statement returns exactly one row. */
15490 SVN_ERR(svn_sqlite__reset(stmt));
15493 *min_revision = min_rev;
15495 *max_revision = max_rev;
15497 return SVN_NO_ERROR;
15502 svn_wc__db_min_max_revisions(svn_revnum_t *min_revision,
15503 svn_revnum_t *max_revision,
15505 const char *local_abspath,
15506 svn_boolean_t committed,
15507 apr_pool_t *scratch_pool)
15509 svn_wc__db_wcroot_t *wcroot;
15510 const char *local_relpath;
15512 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15514 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15516 scratch_pool, scratch_pool));
15517 VERIFY_USABLE_WCROOT(wcroot);
15519 return svn_error_trace(get_min_max_revisions(min_revision, max_revision,
15520 wcroot, local_relpath,
15521 committed, scratch_pool));
15525 /* Set *IS_SPARSE_CHECKOUT TRUE if LOCAL_RELPATH or any of the nodes
15526 * within LOCAL_RELPATH is sparse, FALSE otherwise. */
15527 static svn_error_t *
15528 is_sparse_checkout_internal(svn_boolean_t *is_sparse_checkout,
15529 svn_wc__db_wcroot_t *wcroot,
15530 const char *local_relpath,
15531 apr_pool_t *scratch_pool)
15533 svn_sqlite__stmt_t *stmt;
15534 svn_boolean_t have_row;
15536 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15537 STMT_HAS_SPARSE_NODES));
15538 SVN_ERR(svn_sqlite__bindf(stmt, "is",
15541 /* If this query returns a row, the working copy is sparse. */
15542 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15543 *is_sparse_checkout = have_row;
15544 SVN_ERR(svn_sqlite__reset(stmt));
15546 return SVN_NO_ERROR;
15550 /* Like svn_wc__db_has_switched_subtrees(),
15551 * but accepts a WCROOT/LOCAL_RELPATH pair. */
15552 static svn_error_t *
15553 has_switched_subtrees(svn_boolean_t *is_switched,
15554 svn_wc__db_wcroot_t *wcroot,
15555 const char *local_relpath,
15556 const char *trail_url,
15557 apr_pool_t *scratch_pool)
15559 svn_sqlite__stmt_t *stmt;
15560 svn_boolean_t have_row;
15561 apr_int64_t repos_id;
15562 const char *repos_relpath;
15564 /* Optional argument handling for caller */
15566 return SVN_NO_ERROR;
15568 *is_switched = FALSE;
15570 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
15571 &repos_relpath, &repos_id,
15572 NULL, NULL, NULL, NULL, NULL,
15573 NULL, NULL, NULL, NULL, NULL,
15574 wcroot, local_relpath,
15575 scratch_pool, scratch_pool));
15577 /* First do the cheap check where we only need info on the origin itself */
15578 if (trail_url != NULL)
15580 const char *repos_root_url;
15582 apr_size_t len1, len2;
15584 /* If the trailing part of the URL of the working copy directory
15585 does not match the given trailing URL then the whole working
15586 copy is switched. */
15588 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot,
15589 repos_id, scratch_pool));
15590 url = svn_path_url_add_component2(repos_root_url, repos_relpath,
15593 len1 = strlen(trail_url);
15594 len2 = strlen(url);
15595 if ((len1 > len2) || strcmp(url + len2 - len1, trail_url))
15597 *is_switched = TRUE;
15598 return SVN_NO_ERROR;
15602 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_HAS_SWITCHED));
15603 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath, repos_relpath));
15604 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15606 *is_switched = TRUE;
15607 SVN_ERR(svn_sqlite__reset(stmt));
15609 return SVN_NO_ERROR;
15614 svn_wc__db_has_switched_subtrees(svn_boolean_t *is_switched,
15616 const char *local_abspath,
15617 const char *trail_url,
15618 apr_pool_t *scratch_pool)
15620 svn_wc__db_wcroot_t *wcroot;
15621 const char *local_relpath;
15623 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15625 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15627 scratch_pool, scratch_pool));
15628 VERIFY_USABLE_WCROOT(wcroot);
15630 return svn_error_trace(has_switched_subtrees(is_switched, wcroot,
15631 local_relpath, trail_url,
15636 svn_wc__db_get_excluded_subtrees(apr_hash_t **excluded_subtrees,
15638 const char *local_abspath,
15639 apr_pool_t *result_pool,
15640 apr_pool_t *scratch_pool)
15642 svn_wc__db_wcroot_t *wcroot;
15643 const char *local_relpath;
15644 svn_sqlite__stmt_t *stmt;
15645 svn_boolean_t have_row;
15647 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15648 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15650 scratch_pool, scratch_pool));
15651 VERIFY_USABLE_WCROOT(wcroot);
15653 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15654 STMT_SELECT_ALL_EXCLUDED_DESCENDANTS));
15655 SVN_ERR(svn_sqlite__bindf(stmt, "is",
15658 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15661 *excluded_subtrees = apr_hash_make(result_pool);
15663 *excluded_subtrees = NULL;
15667 const char *abs_path =
15668 svn_dirent_join(wcroot->abspath,
15669 svn_sqlite__column_text(stmt, 0, NULL),
15671 svn_hash_sets(*excluded_subtrees, abs_path, abs_path);
15672 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15675 SVN_ERR(svn_sqlite__reset(stmt));
15676 return SVN_NO_ERROR;
15679 /* Like svn_wc__db_has_db_mods(),
15680 * but accepts a WCROOT/LOCAL_RELPATH pair.
15681 * ### This needs a DB as well as a WCROOT/RELPATH pair... */
15682 static svn_error_t *
15683 has_db_mods(svn_boolean_t *is_modified,
15684 svn_wc__db_wcroot_t *wcroot,
15685 const char *local_relpath,
15686 apr_pool_t *scratch_pool)
15688 svn_sqlite__stmt_t *stmt;
15690 /* Check for additions or deletions. */
15691 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15692 STMT_SUBTREE_HAS_TREE_MODIFICATIONS));
15693 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15694 /* If this query returns a row, the working copy is modified. */
15695 SVN_ERR(svn_sqlite__step(is_modified, stmt));
15696 SVN_ERR(svn_sqlite__reset(stmt));
15698 if (! *is_modified)
15700 /* Check for property modifications. */
15701 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15702 STMT_SUBTREE_HAS_PROP_MODIFICATIONS));
15703 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15704 /* If this query returns a row, the working copy is modified. */
15705 SVN_ERR(svn_sqlite__step(is_modified, stmt));
15706 SVN_ERR(svn_sqlite__reset(stmt));
15709 return SVN_NO_ERROR;
15714 svn_wc__db_has_db_mods(svn_boolean_t *is_modified,
15716 const char *local_abspath,
15717 apr_pool_t *scratch_pool)
15719 svn_wc__db_wcroot_t *wcroot;
15720 const char *local_relpath;
15722 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15724 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15726 scratch_pool, scratch_pool));
15727 VERIFY_USABLE_WCROOT(wcroot);
15729 return svn_error_trace(has_db_mods(is_modified, wcroot, local_relpath,
15734 /* The body of svn_wc__db_revision_status().
15736 static svn_error_t *
15737 revision_status_txn(svn_revnum_t *min_revision,
15738 svn_revnum_t *max_revision,
15739 svn_boolean_t *is_sparse_checkout,
15740 svn_boolean_t *is_modified,
15741 svn_boolean_t *is_switched,
15742 svn_wc__db_wcroot_t *wcroot,
15743 const char *local_relpath,
15745 const char *trail_url,
15746 svn_boolean_t committed,
15747 apr_pool_t *scratch_pool)
15750 svn_boolean_t exists;
15752 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
15756 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
15757 _("The node '%s' was not found."),
15758 path_for_error_message(wcroot, local_relpath,
15762 /* Determine mixed-revisionness. */
15763 SVN_ERR(get_min_max_revisions(min_revision, max_revision, wcroot,
15764 local_relpath, committed, scratch_pool));
15766 /* Determine sparseness. */
15767 SVN_ERR(is_sparse_checkout_internal(is_sparse_checkout, wcroot,
15768 local_relpath, scratch_pool));
15770 /* Check for switched nodes. */
15772 err = has_switched_subtrees(is_switched, wcroot, local_relpath,
15773 trail_url, scratch_pool);
15777 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
15778 return svn_error_trace(err);
15780 svn_error_clear(err); /* No Base node, but no fatal error */
15781 *is_switched = FALSE;
15785 /* Check for db mods. */
15786 SVN_ERR(has_db_mods(is_modified, wcroot, local_relpath, scratch_pool));
15788 return SVN_NO_ERROR;
15793 svn_wc__db_revision_status(svn_revnum_t *min_revision,
15794 svn_revnum_t *max_revision,
15795 svn_boolean_t *is_sparse_checkout,
15796 svn_boolean_t *is_modified,
15797 svn_boolean_t *is_switched,
15799 const char *local_abspath,
15800 const char *trail_url,
15801 svn_boolean_t committed,
15802 apr_pool_t *scratch_pool)
15804 svn_wc__db_wcroot_t *wcroot;
15805 const char *local_relpath;
15807 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15809 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15811 scratch_pool, scratch_pool));
15812 VERIFY_USABLE_WCROOT(wcroot);
15814 SVN_WC__DB_WITH_TXN(
15815 revision_status_txn(min_revision, max_revision,
15816 is_sparse_checkout, is_modified, is_switched,
15817 wcroot, local_relpath, db,
15818 trail_url, committed,
15821 return SVN_NO_ERROR;
15826 svn_wc__db_base_get_lock_tokens_recursive(apr_hash_t **lock_tokens,
15828 const char *local_abspath,
15829 apr_pool_t *result_pool,
15830 apr_pool_t *scratch_pool)
15832 svn_wc__db_wcroot_t *wcroot;
15833 const char *local_relpath;
15834 svn_sqlite__stmt_t *stmt;
15835 svn_boolean_t have_row;
15836 apr_int64_t last_repos_id = INVALID_REPOS_ID;
15837 const char *last_repos_root_url = NULL;
15839 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15841 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15843 scratch_pool, scratch_pool));
15844 VERIFY_USABLE_WCROOT(wcroot);
15846 *lock_tokens = apr_hash_make(result_pool);
15848 /* Fetch all the lock tokens in and under LOCAL_RELPATH. */
15849 SVN_ERR(svn_sqlite__get_statement(
15850 &stmt, wcroot->sdb,
15851 STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE));
15853 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15854 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15857 apr_int64_t child_repos_id = svn_sqlite__column_int64(stmt, 0);
15858 const char *child_relpath = svn_sqlite__column_text(stmt, 1, NULL);
15859 const char *lock_token = svn_sqlite__column_text(stmt, 2, result_pool);
15861 if (child_repos_id != last_repos_id)
15863 svn_error_t *err = svn_wc__db_fetch_repos_info(&last_repos_root_url,
15870 return svn_error_trace(
15871 svn_error_compose_create(err,
15872 svn_sqlite__reset(stmt)));
15875 last_repos_id = child_repos_id;
15878 SVN_ERR_ASSERT(last_repos_root_url != NULL);
15879 svn_hash_sets(*lock_tokens,
15880 svn_path_url_add_component2(last_repos_root_url,
15881 child_relpath, result_pool),
15884 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15886 return svn_sqlite__reset(stmt);
15890 /* If EXPRESSION is false, cause the caller to return an SVN_ERR_WC_CORRUPT
15891 * error, showing EXPRESSION and the caller's LOCAL_RELPATH in the message. */
15892 #define VERIFY(expression) \
15894 if (! (expression)) \
15895 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, \
15896 _("database inconsistency at local_relpath='%s' verifying " \
15897 "expression '%s'"), local_relpath, #expression); \
15901 /* Verify consistency of the metadata concerning WCROOT. This is intended
15902 * for use only during testing and debugging, so is not intended to be
15905 * This code is a complement to any verification that we can do in SQLite
15906 * triggers. See, for example, 'wc-checks.sql'.
15908 * Some more verification steps we might want to add are:
15910 * * on every ACTUAL row (except root): a NODES row exists at its parent path
15911 * * the op-depth root must always exist and every intermediate too
15913 static svn_error_t *
15914 verify_wcroot(svn_wc__db_wcroot_t *wcroot,
15915 apr_pool_t *scratch_pool)
15917 svn_sqlite__stmt_t *stmt;
15918 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15920 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15921 STMT_SELECT_ALL_NODES));
15922 SVN_ERR(svn_sqlite__bindf(stmt, "i", wcroot->wc_id));
15925 svn_boolean_t have_row;
15926 const char *local_relpath, *parent_relpath;
15929 svn_pool_clear(iterpool);
15931 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15935 op_depth = svn_sqlite__column_int(stmt, 0);
15936 local_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
15937 parent_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
15939 /* Verify parent_relpath is the parent path of local_relpath */
15940 VERIFY((parent_relpath == NULL)
15941 ? (local_relpath[0] == '\0')
15942 : (strcmp(svn_relpath_dirname(local_relpath, iterpool),
15943 parent_relpath) == 0));
15945 /* Verify op_depth <= the tree depth of local_relpath */
15946 VERIFY(op_depth <= relpath_depth(local_relpath));
15948 /* Verify parent_relpath refers to a row that exists */
15949 /* TODO: Verify there is a suitable parent row - e.g. has op_depth <=
15950 * the child's and a suitable presence */
15951 if (parent_relpath && svn_sqlite__column_is_null(stmt, 3))
15953 svn_sqlite__stmt_t *stmt2;
15954 svn_boolean_t have_a_parent_row;
15956 SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
15957 STMT_SELECT_NODE_INFO));
15958 SVN_ERR(svn_sqlite__bindf(stmt2, "is", wcroot->wc_id,
15960 SVN_ERR(svn_sqlite__step(&have_a_parent_row, stmt2));
15961 VERIFY(have_a_parent_row);
15962 SVN_ERR(svn_sqlite__reset(stmt2));
15965 svn_pool_destroy(iterpool);
15967 return svn_error_trace(svn_sqlite__reset(stmt));
15971 svn_wc__db_verify(svn_wc__db_t *db,
15972 const char *wri_abspath,
15973 apr_pool_t *scratch_pool)
15975 svn_wc__db_wcroot_t *wcroot;
15976 const char *local_relpath;
15978 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15980 scratch_pool, scratch_pool));
15981 VERIFY_USABLE_WCROOT(wcroot);
15983 SVN_ERR(verify_wcroot(wcroot, scratch_pool));
15984 return SVN_NO_ERROR;
15989 svn_wc__db_verify_db_full_internal(svn_wc__db_wcroot_t *wcroot,
15990 svn_wc__db_verify_cb_t callback,
15992 apr_pool_t *scratch_pool)
15994 svn_sqlite__stmt_t *stmt;
15995 svn_boolean_t have_row;
15996 svn_error_t *err = NULL;
15997 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15999 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_STATIC_VERIFY));
16000 SVN_ERR(svn_sqlite__step(&have_row, stmt));
16004 const char *local_relpath;
16005 int op_depth = svn_sqlite__column_int(stmt, 1);
16006 int id = svn_sqlite__column_int(stmt, 2);
16009 svn_pool_clear(iterpool);
16011 local_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
16012 msg = svn_sqlite__column_text(stmt, 3, scratch_pool);
16014 err = callback(baton, wcroot->abspath, local_relpath, op_depth,
16015 id, msg, iterpool);
16020 SVN_ERR(svn_sqlite__step(&have_row, stmt));
16023 svn_pool_destroy(iterpool);
16025 return svn_error_trace(
16026 svn_error_compose_create(err, svn_sqlite__reset(stmt)));
16030 svn_wc__db_verify_db_full(svn_wc__db_t *db,
16031 const char *wri_abspath,
16032 svn_wc__db_verify_cb_t callback,
16034 apr_pool_t *scratch_pool)
16036 svn_wc__db_wcroot_t *wcroot;
16037 const char *local_relpath;
16039 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
16041 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
16042 wri_abspath, scratch_pool, scratch_pool));
16043 VERIFY_USABLE_WCROOT(wcroot);
16045 return svn_error_trace(
16046 svn_wc__db_verify_db_full_internal(wcroot, callback, baton,
16051 svn_wc__db_bump_format(int *result_format,
16052 svn_boolean_t *bumped_format,
16054 const char *wcroot_abspath,
16055 apr_pool_t *scratch_pool)
16057 svn_sqlite__db_t *sdb;
16062 *bumped_format = FALSE;
16064 /* Do not scan upwards for a working copy root here to prevent accidental
16065 * upgrades of any working copies the WCROOT might be nested in.
16066 * Just try to open a DB at the specified path instead. */
16067 err = svn_wc__db_util_open_db(&sdb, wcroot_abspath, SDB_FILE,
16068 svn_sqlite__mode_readwrite,
16069 TRUE, /* exclusive */
16070 0, /* default timeout */
16071 NULL, /* my statements */
16072 scratch_pool, scratch_pool);
16076 apr_hash_t *entries;
16078 /* Could not open an sdb. Check for an entries file instead. */
16079 err2 = svn_wc__read_entries_old(&entries, wcroot_abspath,
16080 scratch_pool, scratch_pool);
16081 if (err2 || apr_hash_count(entries) == 0)
16082 return svn_error_createf(SVN_ERR_WC_INVALID_OP_ON_CWD,
16083 svn_error_compose_create(err, err2),
16084 _("Can't upgrade '%s' as it is not a working copy root"),
16085 svn_dirent_local_style(wcroot_abspath, scratch_pool));
16087 /* An entries file was found. This is a pre-wc-ng working copy
16088 * so suggest an upgrade. */
16089 return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, err,
16090 _("Working copy '%s' is too old and must be upgraded to "
16091 "at least format %d, as created by Subversion %s"),
16092 svn_dirent_local_style(wcroot_abspath, scratch_pool),
16093 SVN_WC__WC_NG_VERSION,
16094 svn_wc__version_string_from_format(SVN_WC__WC_NG_VERSION));
16097 SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool));
16098 err = svn_wc__upgrade_sdb(result_format, wcroot_abspath,
16099 sdb, format, scratch_pool);
16101 if (err == SVN_NO_ERROR && bumped_format)
16102 *bumped_format = (*result_format > format);
16104 /* Make sure we return a different error than expected for upgrades from
16106 if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
16107 err = svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, err,
16108 _("Working copy upgrade failed"));
16110 err = svn_error_compose_create(err, svn_sqlite__close(sdb));
16112 return svn_error_trace(err);
16116 svn_wc__db_vacuum(svn_wc__db_t *db,
16117 const char *local_abspath,
16118 apr_pool_t *scratch_pool)
16120 svn_wc__db_wcroot_t *wcroot;
16121 const char *local_relpath;
16123 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
16125 scratch_pool, scratch_pool));
16126 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_VACUUM));
16128 return SVN_NO_ERROR;
16131 /* Item queued with svn_wc__db_commit_queue_add */
16132 typedef struct commit_queue_item_t
16134 const char *local_relpath;
16135 svn_boolean_t recurse; /* Use legacy recursion */
16136 svn_boolean_t committed; /* Process the node as committed */
16137 svn_boolean_t remove_lock; /* Remove existing lock on node */
16138 svn_boolean_t remove_changelist; /* Remove changelist on node */
16140 /* The pristine text checksum. NULL if the old value should be kept
16141 and for directories */
16142 const svn_checksum_t *new_sha1_checksum;
16144 apr_hash_t *new_dav_cache; /* New DAV cache for the node */
16145 } commit_queue_item_t;
16147 /* The queue definition for vn_wc__db_create_commit_queue,
16148 svn_wc__db_commit_queue_add and finally svn_wc__db_process_commit_queue */
16149 struct svn_wc__db_commit_queue_t
16151 svn_wc__db_wcroot_t *wcroot; /* Wcroot for ITEMS */
16152 apr_array_header_t *items; /* List of commit_queue_item_t* */
16153 svn_boolean_t have_recurse; /* Is one or more item[x]->recurse TRUE? */
16156 /* Create a new svn_wc__db_commit_queue_t instance in RESULT_POOL for the
16157 working copy specified with WRI_ABSPATH */
16159 svn_wc__db_create_commit_queue(svn_wc__db_commit_queue_t **queue,
16161 const char *wri_abspath,
16162 apr_pool_t *result_pool,
16163 apr_pool_t *scratch_pool)
16165 svn_wc__db_wcroot_t *wcroot;
16166 const char *local_relpath;
16167 svn_wc__db_commit_queue_t *q;
16169 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
16171 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
16172 wri_abspath, result_pool, scratch_pool));
16173 VERIFY_USABLE_WCROOT(wcroot);
16175 q = apr_pcalloc(result_pool, sizeof(*q));
16177 SVN_ERR_ASSERT(wcroot->sdb);
16179 q->wcroot = wcroot;
16180 q->items = apr_array_make(result_pool, 64,
16181 sizeof(commit_queue_item_t*));
16182 q->have_recurse = FALSE;
16185 return SVN_NO_ERROR;
16189 svn_wc__db_commit_queue_add(svn_wc__db_commit_queue_t *queue,
16190 const char *local_abspath,
16191 svn_boolean_t recurse,
16192 svn_boolean_t is_commited,
16193 svn_boolean_t remove_lock,
16194 svn_boolean_t remove_changelist,
16195 const svn_checksum_t *new_sha1_checksum,
16196 apr_hash_t *new_dav_cache,
16197 apr_pool_t *result_pool,
16198 apr_pool_t *scratch_pool)
16200 commit_queue_item_t *cqi;
16201 const char *local_relpath;
16203 local_relpath = svn_dirent_skip_ancestor(queue->wcroot->abspath,
16206 if (! local_relpath)
16207 return svn_error_createf(
16208 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
16209 _("The path '%s' is not in the working copy '%s'"),
16210 svn_dirent_local_style(local_abspath, scratch_pool),
16211 svn_dirent_local_style(queue->wcroot->abspath, scratch_pool));
16213 cqi = apr_pcalloc(result_pool, sizeof(*cqi));
16214 cqi->local_relpath = local_relpath;
16215 cqi->recurse = recurse;
16216 cqi->committed = is_commited;
16217 cqi->remove_lock = remove_lock;
16218 cqi->remove_changelist = remove_changelist;
16219 cqi->new_sha1_checksum = new_sha1_checksum;
16220 cqi->new_dav_cache = new_dav_cache;
16222 queue->have_recurse |= recurse;
16224 APR_ARRAY_PUSH(queue->items, commit_queue_item_t *) = cqi;
16225 return SVN_NO_ERROR;
16228 /*** Finishing updates and commits. ***/
16230 /* Post process an item that is committed in the repository. Collapse layers into
16231 * BASE. Queue work items that will finish a commit of the file or directory
16232 * LOCAL_ABSPATH in DB:
16234 static svn_error_t *
16235 process_committed_leaf(svn_wc__db_t *db,
16236 svn_wc__db_wcroot_t *wcroot,
16237 const char *local_relpath,
16238 svn_boolean_t via_recurse,
16239 svn_wc__db_status_t status,
16240 svn_node_kind_t kind,
16241 svn_boolean_t prop_mods,
16242 const svn_checksum_t *old_checksum,
16243 svn_revnum_t new_revnum,
16244 apr_time_t new_changed_date,
16245 const char *new_changed_author,
16246 apr_hash_t *new_dav_cache,
16247 svn_boolean_t remove_lock,
16248 svn_boolean_t remove_changelist,
16249 const svn_checksum_t *checksum,
16250 apr_pool_t *scratch_pool)
16252 svn_revnum_t new_changed_rev = new_revnum;
16253 svn_skel_t *work_item = NULL;
16256 const char *lock_relpath;
16257 svn_boolean_t locked;
16259 if (kind == svn_node_dir)
16260 lock_relpath = local_relpath;
16262 lock_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
16264 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot,
16265 lock_relpath, FALSE,
16269 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
16270 _("No write-lock in '%s'"),
16271 path_for_error_message(wcroot, local_relpath,
16274 SVN_ERR(flush_entries(wcroot, lock_relpath, svn_depth_empty,
16278 if (status == svn_wc__db_status_not_present)
16280 /* We are committing the leaf of a copy operation.
16281 We leave the not-present marker to allow pulling in excluded
16282 children of a copy.
16284 The next update will remove the not-present marker. */
16286 return SVN_NO_ERROR;
16289 SVN_ERR_ASSERT(status == svn_wc__db_status_normal
16290 || status == svn_wc__db_status_incomplete
16291 || status == svn_wc__db_status_added
16292 || status == svn_wc__db_status_deleted);
16294 if (kind != svn_node_dir
16295 && status != svn_wc__db_status_deleted)
16297 /* If we sent a delta (meaning: post-copy modification),
16298 then this file will appear in the queue and so we should have
16299 its checksum already. */
16300 if (checksum == NULL)
16302 /* It was copied and not modified. We must have a text
16303 base for it. And the node should have a checksum. */
16304 SVN_ERR_ASSERT(old_checksum != NULL);
16306 checksum = old_checksum;
16308 /* Is the node completely unmodified and are we recursing? */
16309 if (via_recurse && !prop_mods)
16311 /* If a copied node itself is not modified, but the op_root of
16312 the copy is committed we have to make sure that changed_rev,
16313 changed_date and changed_author don't change or the working
16314 copy used for committing will show different last modified
16315 information then a clean checkout of exactly the same
16316 revisions. (Issue #3676) */
16318 SVN_ERR(svn_wc__db_read_info_internal(
16319 NULL, NULL, NULL, NULL, NULL,
16322 &new_changed_author, NULL, NULL,
16323 NULL, NULL, NULL, NULL, NULL,
16324 NULL, NULL, NULL, NULL,
16325 NULL, NULL, NULL, NULL,
16327 wcroot, local_relpath,
16328 scratch_pool, scratch_pool));
16332 SVN_ERR(svn_wc__wq_build_file_commit(&work_item,
16333 db, svn_dirent_join(wcroot->abspath,
16337 scratch_pool, scratch_pool));
16340 /* The new text base will be found in the pristine store by its checksum. */
16341 SVN_ERR(commit_node(wcroot, local_relpath,
16342 new_revnum, new_changed_rev,
16343 new_changed_date, new_changed_author,
16346 !remove_changelist,
16351 return SVN_NO_ERROR;
16354 /** Internal helper for svn_wc_process_committed_queue2().
16355 * Bump a commit item, collapsing local changes with the new repository
16356 * information to a new BASE node.
16358 * @a new_date is the (server-side) date of the new revision, or 0.
16360 * @a rev_author is the (server-side) author of the new
16361 * revision; it may be @c NULL.
16363 * @a new_dav_cache is a hash of all the new dav properties for LOCAL_RELPATH.
16365 * If @a remove_lock is set, release any user locks on @a
16366 * local_abspath; otherwise keep them during processing.
16368 * If @a remove_changelist is set, clear any changeset assignments
16369 * from @a local_abspath; otherwise, keep such assignments.
16371 * If @a new_sha1_checksum is non-NULL, use it to identify the node's pristine
16374 * Set TOP_OF_RECURSE to TRUE to show that this the top of a possibly
16375 * recursive commit operation. (Part of the legacy recurse handling)
16377 static svn_error_t *
16378 process_committed_internal(svn_wc__db_t *db,
16379 svn_wc__db_wcroot_t *wcroot,
16380 const char *local_relpath,
16381 svn_boolean_t recurse,
16382 svn_boolean_t top_of_recurse,
16383 svn_revnum_t new_revnum,
16384 apr_time_t new_date,
16385 const char *rev_author,
16386 apr_hash_t *new_dav_cache,
16387 svn_boolean_t remove_lock,
16388 svn_boolean_t remove_changelist,
16389 const svn_checksum_t *new_sha1_checksum,
16390 apr_hash_t *items_by_relpath,
16391 apr_pool_t *scratch_pool)
16393 svn_wc__db_status_t status;
16394 svn_node_kind_t kind;
16395 const svn_checksum_t *old_checksum;
16396 svn_boolean_t prop_mods;
16398 SVN_ERR(svn_wc__db_read_info_internal(&status, &kind, NULL, NULL, NULL, NULL, NULL,
16399 NULL, NULL, &old_checksum, NULL, NULL,
16400 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
16401 NULL, &prop_mods, NULL, NULL, NULL,
16402 wcroot, local_relpath,
16403 scratch_pool, scratch_pool));
16405 /* NOTE: be wary of making crazy semantic changes in this function, since
16406 svn_wc_process_committed4() calls this. */
16408 SVN_ERR(process_committed_leaf(db, wcroot, local_relpath, !top_of_recurse,
16409 status, kind, prop_mods, old_checksum,
16410 new_revnum, new_date, rev_author,
16412 remove_lock, remove_changelist,
16416 /* Only check for recursion on nodes that have children */
16417 if (kind != svn_node_dir
16418 || status == svn_wc__db_status_not_present
16419 || status == svn_wc__db_status_excluded
16420 || status == svn_wc__db_status_server_excluded
16421 /* Node deleted -> then no longer a directory */
16422 || status == svn_wc__db_status_deleted)
16424 return SVN_NO_ERROR;
16429 const apr_array_header_t *children;
16430 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
16433 /* Read PATH's entries; this is the absolute path. */
16434 SVN_ERR(gather_children(&children, wcroot, local_relpath,
16435 STMT_SELECT_NODE_CHILDREN, -1,
16436 scratch_pool, iterpool));
16438 /* Recursively loop over all children. */
16439 for (i = 0; i < children->nelts; i++)
16441 const char *name = APR_ARRAY_IDX(children, i, const char *);
16442 const char *this_relpath;
16443 const commit_queue_item_t *cqi;
16445 svn_pool_clear(iterpool);
16447 this_relpath = svn_dirent_join(local_relpath, name, iterpool);
16449 new_sha1_checksum = NULL;
16450 cqi = svn_hash_gets(items_by_relpath, this_relpath);
16453 new_sha1_checksum = cqi->new_sha1_checksum;
16455 /* Recurse. Pass NULL for NEW_DAV_CACHE, because the
16456 ones present in the current call are only applicable to
16457 this one committed item. */
16458 SVN_ERR(process_committed_internal(
16459 db, wcroot, this_relpath,
16460 TRUE /* recurse */,
16461 FALSE /* top_of_recurse */,
16462 new_revnum, new_date,
16464 NULL /* new_dav_cache */,
16465 FALSE /* remove_lock */,
16472 svn_pool_destroy(iterpool);
16475 return SVN_NO_ERROR;
16478 /* Return TRUE if any item of QUEUE is a parent of ITEM and will be
16479 processed recursively, return FALSE otherwise.
16481 The algorithmic complexity of this search implementation is O(queue
16482 length), but it's quite quick.
16484 static svn_boolean_t
16485 have_recursive_parent(const apr_array_header_t *all_items,
16486 const commit_queue_item_t *item,
16487 apr_pool_t *scratch_pool)
16489 const char *local_relpath = item->local_relpath;
16492 for (i = 0; i < all_items->nelts; i++)
16494 const commit_queue_item_t *qi
16495 = APR_ARRAY_IDX(all_items, i, const commit_queue_item_t *);
16500 if (qi->recurse && svn_relpath_skip_ancestor(qi->local_relpath,
16510 /* Compare function for svn_sort__array */
16512 compare_queue_items(const void *v1,
16515 const commit_queue_item_t *cqi1
16516 = *(const commit_queue_item_t **)v1;
16517 const commit_queue_item_t *cqi2
16518 = *(const commit_queue_item_t **)v2;
16520 return svn_path_compare_paths(cqi1->local_relpath, cqi2->local_relpath);
16523 /* Internal, locked version of svn_wc__db_process_commit_queue */
16524 static svn_error_t *
16525 db_process_commit_queue(svn_wc__db_t *db,
16526 svn_wc__db_commit_queue_t *queue,
16527 svn_revnum_t new_revnum,
16528 apr_time_t new_date,
16529 const char *new_author,
16530 apr_pool_t *scratch_pool)
16532 apr_hash_t *items_by_relpath = NULL;
16534 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
16536 svn_sort__array(queue->items, compare_queue_items);
16538 if (queue->have_recurse)
16540 items_by_relpath = apr_hash_make(scratch_pool);
16542 for (j = 0; j < queue->items->nelts; j++)
16544 commit_queue_item_t *cqi
16545 = APR_ARRAY_IDX(queue->items, j, commit_queue_item_t *);
16547 svn_hash_sets(items_by_relpath, cqi->local_relpath, cqi);
16551 for (j = 0; j < queue->items->nelts; j++)
16553 commit_queue_item_t *cqi
16554 = APR_ARRAY_IDX(queue->items, j, commit_queue_item_t *);
16556 svn_pool_clear(iterpool);
16558 /* Skip this item if it is a child of a recursive item, because it has
16559 been (or will be) accounted for when that recursive item was (or
16560 will be) processed. */
16561 if (queue->have_recurse && have_recursive_parent(queue->items, cqi,
16565 if (!cqi->committed)
16567 if (cqi->remove_lock)
16569 svn_skel_t *work_item;
16571 SVN_ERR(svn_wc__wq_build_sync_file_flags(
16575 queue->wcroot->abspath,
16576 cqi->local_relpath,
16578 iterpool, iterpool));
16580 lock_remove_txn(queue->wcroot, cqi->local_relpath, work_item,
16583 if (cqi->remove_changelist)
16584 SVN_ERR(svn_wc__db_op_set_changelist(db,
16586 queue->wcroot->abspath,
16587 cqi->local_relpath,
16591 NULL, NULL, /* notify */
16592 NULL, NULL, /* cancel */
16597 SVN_ERR(process_committed_internal(
16598 db, queue->wcroot, cqi->local_relpath,
16600 TRUE /* top_of_recurse */,
16601 new_revnum, new_date, new_author,
16602 cqi->new_dav_cache,
16604 cqi->remove_changelist,
16605 cqi->new_sha1_checksum,
16611 svn_pool_destroy(iterpool);
16613 return SVN_NO_ERROR;
16617 svn_wc__db_process_commit_queue(svn_wc__db_t *db,
16618 svn_wc__db_commit_queue_t *queue,
16619 svn_revnum_t new_revnum,
16620 apr_time_t new_date,
16621 const char *new_author,
16622 apr_pool_t *scratch_pool)
16624 SVN_WC__DB_WITH_TXN(db_process_commit_queue(db, queue,
16625 new_revnum, new_date,
16626 new_author, scratch_pool),
16629 return SVN_NO_ERROR;