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));
1375 SVN_ERR(svn_wc__db_install_schema_statistics(db, scratch_pool));
1377 /* Insert the repository. */
1378 SVN_ERR(create_repos_id(repos_id, repos_root_url, repos_uuid,
1381 /* Insert the wcroot. */
1382 /* ### Right now, this just assumes wc metadata is being stored locally. */
1383 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT));
1384 SVN_ERR(svn_sqlite__insert(wc_id, stmt));
1386 if (root_node_repos_relpath)
1388 svn_wc__db_status_t status = svn_wc__db_status_normal;
1390 if (root_node_revision > 0)
1391 status = svn_wc__db_status_incomplete; /* Will be filled by update */
1393 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_NODE));
1394 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtst",
1397 0, /* op_depth is 0 for base */
1398 SVN_VA_NULL, /* 4 */
1400 root_node_repos_relpath,
1402 presence_map, status, /* 8 */
1403 svn_token__to_word(depth_map,
1405 kind_map, svn_node_dir /* 10 */));
1407 SVN_ERR(svn_sqlite__insert(NULL, stmt));
1410 return SVN_NO_ERROR;
1413 /* Create an sqlite database at DIR_ABSPATH/SDB_FNAME and insert
1414 records for REPOS_ID (using REPOS_ROOT_URL and REPOS_UUID) into
1415 REPOSITORY and for WC_ID into WCROOT. Return the DB connection
1418 If ROOT_NODE_REPOS_RELPATH is not NULL, insert a BASE node at
1419 the working copy root with repository relpath ROOT_NODE_REPOS_RELPATH,
1420 revision ROOT_NODE_REVISION and depth ROOT_NODE_DEPTH.
1422 static svn_error_t *
1423 create_db(svn_sqlite__db_t **sdb,
1424 apr_int64_t *repos_id,
1426 const char *dir_abspath,
1427 const char *repos_root_url,
1428 const char *repos_uuid,
1429 const char *sdb_fname,
1430 const char *root_node_repos_relpath,
1431 svn_revnum_t root_node_revision,
1432 svn_depth_t root_node_depth,
1433 svn_boolean_t exclusive,
1434 apr_int32_t timeout,
1435 apr_pool_t *result_pool,
1436 apr_pool_t *scratch_pool)
1438 SVN_ERR(svn_wc__db_util_open_db(sdb, dir_abspath, sdb_fname,
1439 svn_sqlite__mode_rwcreate, exclusive,
1441 NULL /* my_statements */,
1442 result_pool, scratch_pool));
1444 SVN_SQLITE__WITH_LOCK(init_db(repos_id, wc_id,
1445 *sdb, repos_root_url, repos_uuid,
1446 root_node_repos_relpath, root_node_revision,
1447 root_node_depth, scratch_pool),
1450 return SVN_NO_ERROR;
1455 svn_wc__db_init(svn_wc__db_t *db,
1456 const char *local_abspath,
1457 const char *repos_relpath,
1458 const char *repos_root_url,
1459 const char *repos_uuid,
1460 svn_revnum_t initial_rev,
1462 apr_pool_t *scratch_pool)
1464 svn_sqlite__db_t *sdb;
1465 apr_int64_t repos_id;
1467 svn_wc__db_wcroot_t *wcroot;
1468 svn_boolean_t sqlite_exclusive = FALSE;
1469 apr_int32_t sqlite_timeout = 0; /* default timeout */
1470 apr_hash_index_t *hi;
1472 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1473 SVN_ERR_ASSERT(repos_relpath != NULL);
1474 SVN_ERR_ASSERT(depth == svn_depth_empty
1475 || depth == svn_depth_files
1476 || depth == svn_depth_immediates
1477 || depth == svn_depth_infinity);
1479 /* ### REPOS_ROOT_URL and REPOS_UUID may be NULL. ... more doc: tbd */
1481 SVN_ERR(svn_config_get_bool(db->config, &sqlite_exclusive,
1482 SVN_CONFIG_SECTION_WORKING_COPY,
1483 SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
1486 /* Create the SDB and insert the basic rows. */
1487 SVN_ERR(create_db(&sdb, &repos_id, &wc_id, local_abspath, repos_root_url,
1488 repos_uuid, SDB_FILE,
1489 repos_relpath, initial_rev, depth, sqlite_exclusive,
1491 db->state_pool, scratch_pool));
1493 /* Create the WCROOT for this directory. */
1494 SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
1495 apr_pstrdup(db->state_pool, local_abspath),
1496 sdb, wc_id, FORMAT_FROM_SDB,
1497 FALSE /* auto-upgrade */,
1498 db->state_pool, scratch_pool));
1500 /* Any previously cached children may now have a new WCROOT, most likely that
1501 of the new WCROOT, but there might be descendant directories that are their
1502 own working copy, in which case setting WCROOT to our new WCROOT might
1503 actually break things for those.
1505 Clearing is the safest thing we can do in this case, as a test would lead
1506 to unnecessary probing, while the standard code probes later anyway. So we
1507 only lose a bit of memory
1509 ### Perhaps we could check wcroot->abspath to detect which case we have
1510 where, but currently it is already very hard to trigger this from
1511 the short living 'svn' client. (GUI clients like TortoiseSVN are far
1512 more likely to get in these cases)
1514 for (hi = apr_hash_first(scratch_pool, db->dir_data);
1516 hi = apr_hash_next(hi))
1518 const char *abspath = apr_hash_this_key(hi);
1519 if (svn_dirent_is_ancestor(wcroot->abspath, abspath))
1520 svn_hash_sets(db->dir_data, abspath, NULL);
1523 /* The WCROOT is complete. Stash it into DB. */
1524 svn_hash_sets(db->dir_data, wcroot->abspath, wcroot);
1526 return SVN_NO_ERROR;
1531 svn_wc__db_to_relpath(const char **local_relpath,
1533 const char *wri_abspath,
1534 const char *local_abspath,
1535 apr_pool_t *result_pool,
1536 apr_pool_t *scratch_pool)
1538 svn_wc__db_wcroot_t *wcroot;
1539 const char *relpath;
1541 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1543 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &relpath, db,
1544 wri_abspath, result_pool, scratch_pool));
1546 /* This function is indirectly called from the upgrade code, so we
1547 can't verify the wcroot here. Just check that it is not NULL */
1548 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1550 if (svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
1552 *local_relpath = apr_pstrdup(result_pool,
1553 svn_dirent_skip_ancestor(wcroot->abspath,
1557 /* Probably moving from $TMP. Should we allow this? */
1558 *local_relpath = apr_pstrdup(result_pool, local_abspath);
1560 return SVN_NO_ERROR;
1565 svn_wc__db_from_relpath(const char **local_abspath,
1567 const char *wri_abspath,
1568 const char *local_relpath,
1569 apr_pool_t *result_pool,
1570 apr_pool_t *scratch_pool)
1572 svn_wc__db_wcroot_t *wcroot;
1573 const char *unused_relpath;
1575 SVN_ERR_ASSERT(svn_relpath_is_canonical(local_relpath));
1578 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1579 wri_abspath, scratch_pool, scratch_pool));
1581 /* This function is indirectly called from the upgrade code, so we
1582 can't verify the wcroot here. Just check that it is not NULL */
1583 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1586 *local_abspath = svn_dirent_join(wcroot->abspath,
1589 return SVN_NO_ERROR;
1594 svn_wc__db_get_wcroot(const char **wcroot_abspath,
1596 const char *wri_abspath,
1597 apr_pool_t *result_pool,
1598 apr_pool_t *scratch_pool)
1600 svn_wc__db_wcroot_t *wcroot;
1601 const char *unused_relpath;
1603 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1604 wri_abspath, scratch_pool, scratch_pool));
1606 /* Can't use VERIFY_USABLE_WCROOT, as this should be usable to detect
1607 where call upgrade */
1608 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1610 *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
1612 return SVN_NO_ERROR;
1617 svn_wc__db_base_add_directory(svn_wc__db_t *db,
1618 const char *local_abspath,
1619 const char *wri_abspath,
1620 const char *repos_relpath,
1621 const char *repos_root_url,
1622 const char *repos_uuid,
1623 svn_revnum_t revision,
1624 const apr_hash_t *props,
1625 svn_revnum_t changed_rev,
1626 apr_time_t changed_date,
1627 const char *changed_author,
1628 const apr_array_header_t *children,
1630 apr_hash_t *dav_cache,
1631 svn_boolean_t update_actual_props,
1632 apr_hash_t *new_actual_props,
1633 apr_array_header_t *new_iprops,
1634 const svn_skel_t *conflict,
1635 const svn_skel_t *work_items,
1636 apr_pool_t *scratch_pool)
1638 svn_wc__db_wcroot_t *wcroot;
1639 const char *local_relpath;
1640 insert_base_baton_t ibb;
1642 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1643 SVN_ERR_ASSERT(repos_relpath != NULL);
1644 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1645 SVN_ERR_ASSERT(repos_uuid != NULL);
1646 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1647 SVN_ERR_ASSERT(props != NULL);
1648 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1650 SVN_ERR_ASSERT(children != NULL);
1653 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1654 wri_abspath, scratch_pool, scratch_pool));
1655 VERIFY_USABLE_WCROOT(wcroot);
1656 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1660 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1661 ibb.repos_root_url = repos_root_url;
1662 ibb.repos_uuid = repos_uuid;
1664 ibb.status = svn_wc__db_status_normal;
1665 ibb.kind = svn_node_dir;
1666 ibb.repos_relpath = repos_relpath;
1667 ibb.revision = revision;
1669 ibb.iprops = new_iprops;
1671 ibb.changed_rev = changed_rev;
1672 ibb.changed_date = changed_date;
1673 ibb.changed_author = changed_author;
1675 ibb.children = children;
1678 ibb.dav_cache = dav_cache;
1679 ibb.conflict = conflict;
1680 ibb.work_items = work_items;
1682 if (update_actual_props)
1684 ibb.update_actual_props = TRUE;
1685 ibb.new_actual_props = new_actual_props;
1688 /* Insert the directory and all its children transactionally.
1690 Note: old children can stick around, even if they are no longer present
1691 in this directory's revision. */
1692 SVN_WC__DB_WITH_TXN(
1693 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1696 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
1697 return SVN_NO_ERROR;
1701 svn_wc__db_base_add_incomplete_directory(svn_wc__db_t *db,
1702 const char *local_abspath,
1703 const char *repos_relpath,
1704 const char *repos_root_url,
1705 const char *repos_uuid,
1706 svn_revnum_t revision,
1708 svn_boolean_t insert_base_deleted,
1709 svn_boolean_t delete_working,
1710 svn_skel_t *conflict,
1711 svn_skel_t *work_items,
1712 apr_pool_t *scratch_pool)
1714 svn_wc__db_wcroot_t *wcroot;
1715 const char *local_relpath;
1716 struct insert_base_baton_t ibb;
1718 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1719 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1720 SVN_ERR_ASSERT(repos_relpath && repos_root_url && repos_uuid);
1722 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
1724 scratch_pool, scratch_pool));
1726 VERIFY_USABLE_WCROOT(wcroot);
1730 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1731 ibb.repos_root_url = repos_root_url;
1732 ibb.repos_uuid = repos_uuid;
1734 ibb.status = svn_wc__db_status_incomplete;
1735 ibb.kind = svn_node_dir;
1736 ibb.repos_relpath = repos_relpath;
1737 ibb.revision = revision;
1739 ibb.insert_base_deleted = insert_base_deleted;
1740 ibb.delete_working = delete_working;
1742 ibb.conflict = conflict;
1743 ibb.work_items = work_items;
1745 SVN_WC__DB_WITH_TXN(
1746 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1749 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
1751 return SVN_NO_ERROR;
1756 svn_wc__db_base_add_file(svn_wc__db_t *db,
1757 const char *local_abspath,
1758 const char *wri_abspath,
1759 const char *repos_relpath,
1760 const char *repos_root_url,
1761 const char *repos_uuid,
1762 svn_revnum_t revision,
1763 const apr_hash_t *props,
1764 svn_revnum_t changed_rev,
1765 apr_time_t changed_date,
1766 const char *changed_author,
1767 const svn_checksum_t *checksum,
1768 apr_hash_t *dav_cache,
1769 svn_boolean_t delete_working,
1770 svn_boolean_t update_actual_props,
1771 apr_hash_t *new_actual_props,
1772 apr_array_header_t *new_iprops,
1773 svn_boolean_t keep_recorded_info,
1774 svn_boolean_t insert_base_deleted,
1775 const svn_skel_t *conflict,
1776 const svn_skel_t *work_items,
1777 apr_pool_t *scratch_pool)
1779 svn_wc__db_wcroot_t *wcroot;
1780 const char *local_relpath;
1781 insert_base_baton_t ibb;
1783 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1784 SVN_ERR_ASSERT(repos_relpath != NULL);
1785 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1786 SVN_ERR_ASSERT(repos_uuid != NULL);
1787 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1788 SVN_ERR_ASSERT(props != NULL);
1789 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1790 SVN_ERR_ASSERT(checksum != NULL);
1792 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1793 wri_abspath, scratch_pool, scratch_pool));
1794 VERIFY_USABLE_WCROOT(wcroot);
1795 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1799 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1800 ibb.repos_root_url = repos_root_url;
1801 ibb.repos_uuid = repos_uuid;
1803 ibb.status = svn_wc__db_status_normal;
1804 ibb.kind = svn_node_file;
1805 ibb.repos_relpath = repos_relpath;
1806 ibb.revision = revision;
1809 ibb.changed_rev = changed_rev;
1810 ibb.changed_date = changed_date;
1811 ibb.changed_author = changed_author;
1813 ibb.checksum = checksum;
1815 ibb.dav_cache = dav_cache;
1816 ibb.iprops = new_iprops;
1818 if (update_actual_props)
1820 ibb.update_actual_props = TRUE;
1821 ibb.new_actual_props = new_actual_props;
1824 ibb.keep_recorded_info = keep_recorded_info;
1825 ibb.insert_base_deleted = insert_base_deleted;
1826 ibb.delete_working = delete_working;
1828 ibb.conflict = conflict;
1829 ibb.work_items = work_items;
1831 SVN_WC__DB_WITH_TXN(
1832 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1835 /* If this used to be a directory we should remove children so pass
1836 * depth infinity. */
1837 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1839 return SVN_NO_ERROR;
1844 svn_wc__db_base_add_symlink(svn_wc__db_t *db,
1845 const char *local_abspath,
1846 const char *wri_abspath,
1847 const char *repos_relpath,
1848 const char *repos_root_url,
1849 const char *repos_uuid,
1850 svn_revnum_t revision,
1851 const apr_hash_t *props,
1852 svn_revnum_t changed_rev,
1853 apr_time_t changed_date,
1854 const char *changed_author,
1856 apr_hash_t *dav_cache,
1857 svn_boolean_t delete_working,
1858 svn_boolean_t update_actual_props,
1859 apr_hash_t *new_actual_props,
1860 apr_array_header_t *new_iprops,
1861 svn_boolean_t keep_recorded_info,
1862 svn_boolean_t insert_base_deleted,
1863 const svn_skel_t *conflict,
1864 const svn_skel_t *work_items,
1865 apr_pool_t *scratch_pool)
1867 svn_wc__db_wcroot_t *wcroot;
1868 const char *local_relpath;
1869 insert_base_baton_t ibb;
1871 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1872 SVN_ERR_ASSERT(repos_relpath != NULL);
1873 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1874 SVN_ERR_ASSERT(repos_uuid != NULL);
1875 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1876 SVN_ERR_ASSERT(props != NULL);
1877 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1878 SVN_ERR_ASSERT(target != NULL);
1880 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1881 wri_abspath, scratch_pool, scratch_pool));
1882 VERIFY_USABLE_WCROOT(wcroot);
1883 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1886 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1887 ibb.repos_root_url = repos_root_url;
1888 ibb.repos_uuid = repos_uuid;
1890 ibb.status = svn_wc__db_status_normal;
1891 ibb.kind = svn_node_symlink;
1892 ibb.repos_relpath = repos_relpath;
1893 ibb.revision = revision;
1896 ibb.changed_rev = changed_rev;
1897 ibb.changed_date = changed_date;
1898 ibb.changed_author = changed_author;
1900 ibb.target = target;
1902 ibb.dav_cache = dav_cache;
1903 ibb.iprops = new_iprops;
1905 if (update_actual_props)
1907 ibb.update_actual_props = TRUE;
1908 ibb.new_actual_props = new_actual_props;
1911 ibb.keep_recorded_info = keep_recorded_info;
1912 ibb.insert_base_deleted = insert_base_deleted;
1913 ibb.delete_working = delete_working;
1915 ibb.conflict = conflict;
1916 ibb.work_items = work_items;
1918 SVN_WC__DB_WITH_TXN(
1919 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1922 /* If this used to be a directory we should remove children so pass
1923 * depth infinity. */
1924 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1926 return SVN_NO_ERROR;
1930 static svn_error_t *
1931 add_excluded_or_not_present_node(svn_wc__db_t *db,
1932 const char *local_abspath,
1933 const char *repos_relpath,
1934 const char *repos_root_url,
1935 const char *repos_uuid,
1936 svn_revnum_t revision,
1937 svn_node_kind_t kind,
1938 svn_wc__db_status_t status,
1939 const svn_skel_t *conflict,
1940 const svn_skel_t *work_items,
1941 apr_pool_t *scratch_pool)
1943 svn_wc__db_wcroot_t *wcroot;
1944 const char *local_relpath;
1945 insert_base_baton_t ibb;
1946 const char *dir_abspath, *name;
1948 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1949 SVN_ERR_ASSERT(repos_relpath != NULL);
1950 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1951 SVN_ERR_ASSERT(repos_uuid != NULL);
1952 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1953 SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
1954 || status == svn_wc__db_status_excluded
1955 || status == svn_wc__db_status_not_present);
1957 /* These absent presence nodes are only useful below a parent node that is
1958 present. To avoid problems with working copies obstructing the child
1959 we calculate the wcroot and local_relpath of the parent and then add
1962 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
1964 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1965 dir_abspath, scratch_pool, scratch_pool));
1966 VERIFY_USABLE_WCROOT(wcroot);
1968 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
1972 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1973 ibb.repos_root_url = repos_root_url;
1974 ibb.repos_uuid = repos_uuid;
1976 ibb.status = status;
1978 ibb.repos_relpath = repos_relpath;
1979 ibb.revision = revision;
1981 /* Depending upon KIND, any of these might get used. */
1982 ibb.children = NULL;
1983 ibb.depth = svn_depth_unknown;
1984 ibb.checksum = NULL;
1987 ibb.conflict = conflict;
1988 ibb.work_items = work_items;
1990 SVN_WC__DB_WITH_TXN(
1991 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1994 /* If this used to be a directory we should remove children so pass
1995 * depth infinity. */
1996 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1999 return SVN_NO_ERROR;
2004 svn_wc__db_base_add_excluded_node(svn_wc__db_t *db,
2005 const char *local_abspath,
2006 const char *repos_relpath,
2007 const char *repos_root_url,
2008 const char *repos_uuid,
2009 svn_revnum_t revision,
2010 svn_node_kind_t kind,
2011 svn_wc__db_status_t status,
2012 const svn_skel_t *conflict,
2013 const svn_skel_t *work_items,
2014 apr_pool_t *scratch_pool)
2016 SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
2017 || status == svn_wc__db_status_excluded);
2019 return add_excluded_or_not_present_node(
2020 db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2021 kind, status, conflict, work_items, scratch_pool);
2026 svn_wc__db_base_add_not_present_node(svn_wc__db_t *db,
2027 const char *local_abspath,
2028 const char *repos_relpath,
2029 const char *repos_root_url,
2030 const char *repos_uuid,
2031 svn_revnum_t revision,
2032 svn_node_kind_t kind,
2033 const svn_skel_t *conflict,
2034 const svn_skel_t *work_items,
2035 apr_pool_t *scratch_pool)
2037 return add_excluded_or_not_present_node(
2038 db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2039 kind, svn_wc__db_status_not_present, conflict, work_items, scratch_pool);
2042 /* Recursively clear moved-here information at the copy-half of the move
2043 * which moved a node to MOVED_TO_RELPATH. This transforms this side of the
2044 * move into a simple copy.
2046 static svn_error_t *
2047 clear_moved_here(svn_wc__db_wcroot_t *wcroot,
2048 const char *moved_to_relpath,
2049 apr_pool_t *scratch_pool)
2051 svn_sqlite__stmt_t *stmt;
2054 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2055 STMT_CLEAR_MOVED_HERE_RECURSIVE));
2056 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, moved_to_relpath,
2057 relpath_depth(moved_to_relpath)));
2059 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2061 if (affected_rows == 0)
2062 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2063 _("The node '%s' was not found."),
2064 path_for_error_message(wcroot, moved_to_relpath,
2067 return SVN_NO_ERROR;
2071 svn_wc__db_op_break_move_internal(svn_wc__db_wcroot_t *wcroot,
2072 const char *src_relpath,
2073 int delete_op_depth,
2074 const char *dst_relpath,
2075 const svn_skel_t *work_items,
2076 apr_pool_t *scratch_pool)
2078 svn_sqlite__stmt_t *stmt;
2081 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2082 STMT_CLEAR_MOVED_TO_RELPATH));
2083 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, src_relpath,
2085 SVN_ERR(svn_sqlite__update(&affected, stmt));
2088 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2089 _("Path '%s' is not moved"),
2090 path_for_error_message(wcroot, src_relpath,
2093 SVN_ERR(clear_moved_here(wcroot, dst_relpath, scratch_pool));
2095 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2096 return SVN_NO_ERROR;
2100 /* The body of svn_wc__db_base_remove().
2102 static svn_error_t *
2103 db_base_remove(svn_wc__db_wcroot_t *wcroot,
2104 const char *local_relpath,
2105 svn_wc__db_t *db, /* For checking conflicts */
2106 svn_boolean_t keep_as_working,
2107 svn_boolean_t mark_not_present,
2108 svn_boolean_t mark_excluded,
2109 svn_revnum_t marker_revision,
2110 svn_skel_t *conflict,
2111 svn_skel_t *work_items,
2112 apr_pool_t *scratch_pool)
2114 svn_sqlite__stmt_t *stmt;
2115 svn_boolean_t have_row;
2116 svn_wc__db_status_t status;
2117 svn_revnum_t revision;
2118 apr_int64_t repos_id;
2119 const char *repos_relpath;
2120 svn_node_kind_t kind;
2121 svn_boolean_t keep_working;
2123 svn_node_kind_t wrk_kind;
2124 svn_boolean_t no_delete_wc = FALSE;
2125 svn_boolean_t file_external;
2127 SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, &revision,
2128 &repos_relpath, &repos_id,
2129 NULL, NULL, NULL, NULL, NULL,
2130 NULL, NULL, NULL, NULL,
2132 wcroot, local_relpath,
2133 scratch_pool, scratch_pool));
2135 /* Check if there is already a working node */
2136 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2137 STMT_SELECT_NODE_INFO));
2138 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2139 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2142 return svn_error_trace(svn_sqlite__reset(stmt)); /* No BASE */
2144 op_depth = svn_sqlite__column_int(stmt, 0);
2145 wrk_kind = svn_sqlite__column_token(stmt, 4, kind_map);
2148 && op_depth == relpath_depth(local_relpath))
2150 svn_wc__db_status_t presence;
2151 presence = svn_sqlite__column_token(stmt, 3, presence_map);
2153 if (presence == svn_wc__db_status_base_deleted)
2155 keep_working = FALSE;
2156 no_delete_wc = TRUE;
2160 keep_working = TRUE;
2164 keep_working = FALSE;
2165 SVN_ERR(svn_sqlite__reset(stmt));
2167 if (keep_as_working && op_depth == 0)
2169 if (status == svn_wc__db_status_normal
2170 || status == svn_wc__db_status_incomplete)
2172 SVN_ERR(svn_wc__db_op_make_copy_internal(wcroot, local_relpath, TRUE,
2176 keep_working = TRUE;
2179 /* Step 1: Create workqueue operations to remove files and dirs in the
2181 if (!keep_working && !no_delete_wc)
2183 svn_skel_t *work_item;
2184 const char *local_abspath;
2186 local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
2188 if (wrk_kind == svn_node_dir)
2190 apr_pool_t *iterpool;
2191 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2192 STMT_SELECT_WORKING_PRESENT));
2193 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2195 iterpool = svn_pool_create(scratch_pool);
2197 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2201 const char *node_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2202 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 1,
2204 const char *node_abspath;
2207 svn_pool_clear(iterpool);
2209 node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
2212 if (node_kind == svn_node_dir)
2213 err = svn_wc__wq_build_dir_remove(&work_item,
2214 db, wcroot->abspath,
2215 node_abspath, FALSE,
2216 iterpool, iterpool);
2218 err = svn_wc__wq_build_file_remove(&work_item,
2222 iterpool, iterpool);
2225 err = add_work_items(wcroot->sdb, work_item, iterpool);
2227 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2229 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2232 SVN_ERR(svn_sqlite__reset(stmt));
2234 SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
2235 db, wcroot->abspath,
2236 local_abspath, FALSE,
2237 scratch_pool, iterpool));
2238 svn_pool_destroy(iterpool);
2241 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
2242 db, wcroot->abspath,
2244 scratch_pool, scratch_pool));
2246 SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
2249 /* Step 2: Delete ACTUAL nodes */
2252 /* There won't be a record in NODE left for this node, so we want
2253 to remove *all* ACTUAL nodes, including ACTUAL ONLY. */
2254 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2255 STMT_DELETE_ACTUAL_NODE_RECURSIVE));
2256 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2257 SVN_ERR(svn_sqlite__step_done(stmt));
2259 else if (! keep_as_working)
2261 /* Delete only the ACTUAL nodes that apply to a delete of a BASE node */
2262 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2263 STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE));
2264 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2265 SVN_ERR(svn_sqlite__step_done(stmt));
2267 /* Else: Everything has been turned into a copy, so we want to keep all
2268 ACTUAL_NODE records */
2270 /* Step 3: Delete WORKING nodes */
2273 apr_pool_t *iterpool;
2275 /* When deleting everything in working we should break moves from
2278 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2279 STMT_SELECT_MOVED_OUTSIDE));
2280 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2282 relpath_depth(local_relpath)));
2283 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2284 iterpool = svn_pool_create(scratch_pool);
2287 const char *moved_to_relpath;
2290 svn_pool_clear(iterpool);
2291 moved_to_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
2292 err = clear_moved_here(wcroot, moved_to_relpath, iterpool);
2294 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2295 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2297 svn_pool_destroy(iterpool);
2298 SVN_ERR(svn_sqlite__reset(stmt));
2302 /* We are keeping things that are in WORKING, but we should still
2303 break moves of things in BASE. (Mixed revisions make it
2304 impossible to guarantee that we can keep everything moved) */
2306 apr_pool_t *iterpool;
2308 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2309 STMT_SELECT_MOVED_DESCENDANTS_SRC));
2310 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2312 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2313 iterpool = svn_pool_create(scratch_pool);
2316 int delete_op_depth = svn_sqlite__column_int(stmt, 0);
2317 const char *src_relpath;
2318 const char *dst_relpath;
2321 svn_pool_clear(iterpool);
2323 src_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
2324 dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool);
2326 err = svn_wc__db_op_break_move_internal(wcroot, src_relpath,
2333 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2335 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2337 svn_pool_destroy(iterpool);
2338 SVN_ERR(svn_sqlite__reset(stmt));
2342 SVN_ERR(svn_sqlite__get_statement(
2344 STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE));
2345 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
2346 SVN_ERR(svn_sqlite__step_done(stmt));
2350 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2351 STMT_DELETE_WORKING_RECURSIVE));
2352 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2353 SVN_ERR(svn_sqlite__step_done(stmt));
2356 /* Step 4: Delete the BASE node descendants */
2357 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2358 STMT_DELETE_BASE_RECURSIVE));
2359 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2360 SVN_ERR(svn_sqlite__step_done(stmt));
2362 SVN_ERR(db_retract_parent_delete(wcroot, local_relpath, 0, scratch_pool));
2364 if (mark_not_present || mark_excluded)
2366 struct insert_base_baton_t ibb;
2367 svn_boolean_t no_marker = FALSE;
2371 const char *parent_local_relpath;
2375 /* For file externals we only want to place a not present marker
2376 if there is a BASE parent */
2378 svn_relpath_split(&parent_local_relpath, &name, local_relpath,
2381 err = svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
2382 &repos_relpath, &repos_id,
2383 NULL, NULL, NULL, NULL, NULL,
2384 NULL, NULL, NULL, NULL, NULL,
2385 wcroot, parent_local_relpath,
2386 scratch_pool, scratch_pool);
2388 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2389 return svn_error_trace(err);
2392 svn_error_clear(err);
2397 /* Replace the repos_relpath with something more expected than
2398 the unrelated old file external repository relpath, which
2399 one day may come from a different repository */
2400 repos_relpath = svn_relpath_join(repos_relpath, name, scratch_pool);
2408 ibb.repos_id = repos_id;
2409 ibb.status = mark_excluded ? svn_wc__db_status_excluded
2410 : svn_wc__db_status_not_present;
2412 ibb.repos_relpath = repos_relpath;
2413 ibb.revision = SVN_IS_VALID_REVNUM(marker_revision)
2417 /* Depending upon KIND, any of these might get used. */
2418 ibb.children = NULL;
2419 ibb.depth = svn_depth_unknown;
2420 ibb.checksum = NULL;
2423 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
2427 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2429 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
2430 conflict, scratch_pool));
2432 return SVN_NO_ERROR;
2437 svn_wc__db_base_remove(svn_wc__db_t *db,
2438 const char *local_abspath,
2439 svn_boolean_t keep_as_working,
2440 svn_boolean_t mark_not_present,
2441 svn_boolean_t mark_excluded,
2442 svn_revnum_t marker_revision,
2443 svn_skel_t *conflict,
2444 svn_skel_t *work_items,
2445 apr_pool_t *scratch_pool)
2447 svn_wc__db_wcroot_t *wcroot;
2448 const char *local_relpath;
2450 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2452 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2453 local_abspath, scratch_pool, scratch_pool));
2454 VERIFY_USABLE_WCROOT(wcroot);
2456 SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath,
2457 db, keep_as_working,
2458 mark_not_present, mark_excluded,
2460 conflict, work_items, scratch_pool),
2463 /* If this used to be a directory we should remove children so pass
2464 * depth infinity. */
2465 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2468 return SVN_NO_ERROR;
2473 svn_wc__db_base_get_info_internal(svn_wc__db_status_t *status,
2474 svn_node_kind_t *kind,
2475 svn_revnum_t *revision,
2476 const char **repos_relpath,
2477 apr_int64_t *repos_id,
2478 svn_revnum_t *changed_rev,
2479 apr_time_t *changed_date,
2480 const char **changed_author,
2482 const svn_checksum_t **checksum,
2483 const char **target,
2484 svn_wc__db_lock_t **lock,
2485 svn_boolean_t *had_props,
2487 svn_boolean_t *update_root,
2488 svn_wc__db_wcroot_t *wcroot,
2489 const char *local_relpath,
2490 apr_pool_t *result_pool,
2491 apr_pool_t *scratch_pool)
2493 svn_sqlite__stmt_t *stmt;
2494 svn_boolean_t have_row;
2495 svn_error_t *err = SVN_NO_ERROR;
2497 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2498 lock ? STMT_SELECT_BASE_NODE_WITH_LOCK
2499 : STMT_SELECT_BASE_NODE));
2500 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2501 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2505 svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2507 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2515 *status = node_status;
2517 repos_location_from_columns(repos_id, revision, repos_relpath,
2518 stmt, 0, 4, 1, result_pool);
2519 SVN_ERR_ASSERT(!repos_id || *repos_id != INVALID_REPOS_ID);
2520 SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
2523 *lock = lock_from_columns(stmt, 15, 16, 17, 18, result_pool);
2527 *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2531 *changed_date = svn_sqlite__column_int64(stmt, 8);
2535 /* Result may be NULL. */
2536 *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2540 if (node_kind != svn_node_dir)
2542 *depth = svn_depth_unknown;
2546 *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2552 if (node_kind != svn_node_file)
2558 err = svn_sqlite__column_checksum(checksum, stmt, 5,
2561 err = svn_error_createf(
2563 _("The node '%s' has a corrupt checksum value."),
2564 path_for_error_message(wcroot, local_relpath,
2570 if (node_kind != svn_node_symlink)
2573 *target = svn_sqlite__column_text(stmt, 11, result_pool);
2577 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2581 if (node_status == svn_wc__db_status_normal
2582 || node_status == svn_wc__db_status_incomplete)
2584 SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2585 result_pool, scratch_pool));
2587 *props = apr_hash_make(result_pool);
2591 assert(svn_sqlite__column_is_null(stmt, 13));
2597 /* It's an update root iff it's a file external. */
2598 *update_root = svn_sqlite__column_boolean(stmt, 14);
2603 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2604 _("The node '%s' was not found."),
2605 path_for_error_message(wcroot, local_relpath,
2609 /* Note: given the composition, no need to wrap for tracing. */
2610 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2615 svn_wc__db_base_get_info(svn_wc__db_status_t *status,
2616 svn_node_kind_t *kind,
2617 svn_revnum_t *revision,
2618 const char **repos_relpath,
2619 const char **repos_root_url,
2620 const char **repos_uuid,
2621 svn_revnum_t *changed_rev,
2622 apr_time_t *changed_date,
2623 const char **changed_author,
2625 const svn_checksum_t **checksum,
2626 const char **target,
2627 svn_wc__db_lock_t **lock,
2628 svn_boolean_t *had_props,
2630 svn_boolean_t *update_root,
2632 const char *local_abspath,
2633 apr_pool_t *result_pool,
2634 apr_pool_t *scratch_pool)
2636 svn_wc__db_wcroot_t *wcroot;
2637 const char *local_relpath;
2638 apr_int64_t repos_id;
2640 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2642 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2643 local_abspath, scratch_pool, scratch_pool));
2644 VERIFY_USABLE_WCROOT(wcroot);
2646 SVN_WC__DB_WITH_TXN4(
2647 svn_wc__db_base_get_info_internal(status, kind, revision,
2648 repos_relpath, &repos_id,
2649 changed_rev, changed_date,
2650 changed_author, depth,
2651 checksum, target, lock,
2652 had_props, props, update_root,
2653 wcroot, local_relpath,
2654 result_pool, scratch_pool),
2655 svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
2656 wcroot, repos_id, result_pool),
2660 SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
2662 return SVN_NO_ERROR;
2665 /* The implementation of svn_wc__db_base_get_children_info */
2666 static svn_error_t *
2667 base_get_children_info(apr_hash_t **nodes,
2668 svn_wc__db_wcroot_t *wcroot,
2669 const char *local_relpath,
2670 svn_boolean_t obtain_locks,
2671 apr_pool_t *result_pool,
2672 apr_pool_t *scratch_pool)
2674 svn_sqlite__stmt_t *stmt;
2675 svn_boolean_t have_row;
2676 apr_int64_t last_repos_id = INVALID_REPOS_ID;
2677 const char *last_repos_root_url = NULL;
2679 *nodes = apr_hash_make(result_pool);
2681 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2683 ? STMT_SELECT_BASE_CHILDREN_INFO_LOCK
2684 : STMT_SELECT_BASE_CHILDREN_INFO));
2685 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2687 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2691 struct svn_wc__db_base_info_t *info;
2692 apr_int64_t repos_id;
2693 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2694 const char *name = svn_relpath_basename(child_relpath, result_pool);
2696 info = apr_pcalloc(result_pool, sizeof(*info));
2698 repos_id = svn_sqlite__column_int64(stmt, 1);
2699 info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
2700 info->status = svn_sqlite__column_token(stmt, 3, presence_map);
2701 info->kind = svn_sqlite__column_token(stmt, 4, kind_map);
2702 info->revnum = svn_sqlite__column_revnum(stmt, 5);
2704 info->depth = svn_sqlite__column_token_null(stmt, 6, depth_map,
2707 info->update_root = svn_sqlite__column_boolean(stmt, 7);
2710 info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool);
2712 if (repos_id != last_repos_id)
2716 err = svn_wc__db_fetch_repos_info(&last_repos_root_url, NULL,
2721 return svn_error_trace(
2722 svn_error_compose_create(err,
2723 svn_sqlite__reset(stmt)));
2725 last_repos_id = repos_id;
2728 info->repos_root_url = last_repos_root_url;
2730 svn_hash_sets(*nodes, name, info);
2732 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2735 SVN_ERR(svn_sqlite__reset(stmt));
2737 return SVN_NO_ERROR;
2741 svn_wc__db_base_get_children_info(apr_hash_t **nodes,
2743 const char *dir_abspath,
2744 apr_pool_t *result_pool,
2745 apr_pool_t *scratch_pool)
2747 svn_wc__db_wcroot_t *wcroot;
2748 const char *local_relpath;
2750 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
2752 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2753 dir_abspath, scratch_pool, scratch_pool));
2754 VERIFY_USABLE_WCROOT(wcroot);
2756 return svn_error_trace(base_get_children_info(nodes,
2759 TRUE /* obtain_locks */,
2766 svn_wc__db_base_get_props(apr_hash_t **props,
2768 const char *local_abspath,
2769 apr_pool_t *result_pool,
2770 apr_pool_t *scratch_pool)
2772 svn_wc__db_status_t presence;
2774 SVN_ERR(svn_wc__db_base_get_info(&presence, NULL, NULL, NULL, NULL,
2775 NULL, NULL, NULL, NULL, NULL,
2776 NULL, NULL, NULL, NULL, props, NULL,
2778 result_pool, scratch_pool));
2779 if (presence != svn_wc__db_status_normal
2780 && presence != svn_wc__db_status_incomplete)
2782 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2783 _("The node '%s' has a BASE status that"
2784 " has no properties."),
2785 svn_dirent_local_style(local_abspath,
2789 return SVN_NO_ERROR;
2794 svn_wc__db_base_get_children(const apr_array_header_t **children,
2796 const char *local_abspath,
2797 apr_pool_t *result_pool,
2798 apr_pool_t *scratch_pool)
2800 svn_wc__db_wcroot_t *wcroot;
2801 const char *local_relpath;
2803 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2805 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2807 scratch_pool, scratch_pool));
2808 VERIFY_USABLE_WCROOT(wcroot);
2810 return svn_error_trace(
2811 gather_children(children, wcroot, local_relpath,
2812 STMT_SELECT_OP_DEPTH_CHILDREN, 0,
2813 result_pool, scratch_pool));
2818 svn_wc__db_base_set_dav_cache(svn_wc__db_t *db,
2819 const char *local_abspath,
2820 const apr_hash_t *props,
2821 apr_pool_t *scratch_pool)
2823 svn_wc__db_wcroot_t *wcroot;
2824 const char *local_relpath;
2825 svn_sqlite__stmt_t *stmt;
2828 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2830 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2831 local_abspath, scratch_pool, scratch_pool));
2832 VERIFY_USABLE_WCROOT(wcroot);
2834 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2835 STMT_UPDATE_BASE_NODE_DAV_CACHE));
2836 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2837 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
2839 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2841 if (affected_rows != 1)
2842 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2843 _("The node '%s' was not found."),
2844 svn_dirent_local_style(local_abspath,
2847 return SVN_NO_ERROR;
2852 svn_wc__db_base_get_dav_cache(apr_hash_t **props,
2854 const char *local_abspath,
2855 apr_pool_t *result_pool,
2856 apr_pool_t *scratch_pool)
2858 svn_wc__db_wcroot_t *wcroot;
2859 const char *local_relpath;
2860 svn_sqlite__stmt_t *stmt;
2861 svn_boolean_t have_row;
2863 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2865 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2866 local_abspath, scratch_pool, scratch_pool));
2867 VERIFY_USABLE_WCROOT(wcroot);
2869 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2870 STMT_SELECT_BASE_DAV_CACHE));
2872 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2874 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
2875 svn_sqlite__reset(stmt),
2876 _("The node '%s' was not found."),
2877 svn_dirent_local_style(local_abspath,
2880 SVN_ERR(svn_sqlite__column_properties(props, stmt, 0, result_pool,
2882 return svn_error_trace(svn_sqlite__reset(stmt));
2887 svn_wc__db_base_clear_dav_cache_recursive(svn_wc__db_t *db,
2888 const char *local_abspath,
2889 apr_pool_t *scratch_pool)
2891 svn_wc__db_wcroot_t *wcroot;
2892 const char *local_relpath;
2893 svn_sqlite__stmt_t *stmt;
2895 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2897 scratch_pool, scratch_pool));
2898 VERIFY_USABLE_WCROOT(wcroot);
2900 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2901 STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE));
2902 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2904 SVN_ERR(svn_sqlite__step_done(stmt));
2906 return SVN_NO_ERROR;
2911 svn_wc__db_depth_get_info(svn_wc__db_status_t *status,
2912 svn_node_kind_t *kind,
2913 svn_revnum_t *revision,
2914 const char **repos_relpath,
2915 apr_int64_t *repos_id,
2916 svn_revnum_t *changed_rev,
2917 apr_time_t *changed_date,
2918 const char **changed_author,
2920 const svn_checksum_t **checksum,
2921 const char **target,
2922 svn_boolean_t *had_props,
2924 svn_wc__db_wcroot_t *wcroot,
2925 const char *local_relpath,
2927 apr_pool_t *result_pool,
2928 apr_pool_t *scratch_pool)
2930 svn_sqlite__stmt_t *stmt;
2931 svn_boolean_t have_row;
2932 svn_error_t *err = SVN_NO_ERROR;
2934 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2935 STMT_SELECT_DEPTH_NODE));
2936 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
2937 wcroot->wc_id, local_relpath, op_depth));
2938 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2942 svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2944 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2952 *status = node_status;
2955 SVN_ERR(convert_to_working_status(status, *status));
2957 repos_location_from_columns(repos_id, revision, repos_relpath,
2958 stmt, 0, 4, 1, result_pool);
2962 *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2966 *changed_date = svn_sqlite__column_int64(stmt, 8);
2970 /* Result may be NULL. */
2971 *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2975 if (node_kind != svn_node_dir)
2977 *depth = svn_depth_unknown;
2981 *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2987 if (node_kind != svn_node_file)
2993 err = svn_sqlite__column_checksum(checksum, stmt, 5,
2996 err = svn_error_createf(
2998 _("The node '%s' has a corrupt checksum value."),
2999 path_for_error_message(wcroot, local_relpath,
3005 if (node_kind != svn_node_symlink)
3008 *target = svn_sqlite__column_text(stmt, 11, result_pool);
3012 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 12);
3016 if (node_status == svn_wc__db_status_normal
3017 || node_status == svn_wc__db_status_incomplete)
3019 SVN_ERR(svn_sqlite__column_properties(props, stmt, 12,
3020 result_pool, scratch_pool));
3022 *props = apr_hash_make(result_pool);
3026 assert(svn_sqlite__column_is_null(stmt, 12));
3033 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3034 _("The node '%s' was not found."),
3035 path_for_error_message(wcroot, local_relpath,
3039 /* Note: given the composition, no need to wrap for tracing. */
3040 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
3043 /* A callback which supplies WCROOTs and LOCAL_RELPATHs. */
3044 typedef svn_error_t *(*svn_wc__db_txn_callback_t)(void *baton,
3045 svn_wc__db_wcroot_t *wcroot,
3046 const char *local_relpath,
3047 apr_pool_t *scratch_pool);
3049 /* Baton for passing args to with_triggers(). */
3050 struct with_triggers_baton_t {
3053 svn_wc__db_txn_callback_t cb_func;
3057 /* Helper for creating SQLite triggers, running the main transaction
3058 callback, and then dropping the triggers. It guarantees that the
3059 triggers will not survive the transaction. This could be used for
3060 any general prefix/postscript statements where the postscript
3061 *must* be executed if the transaction completes.
3063 Implements svn_wc__db_txn_callback_t. */
3064 static svn_error_t *
3065 with_triggers(void *baton,
3066 svn_wc__db_wcroot_t *wcroot,
3067 const char *local_relpath,
3068 apr_pool_t *scratch_pool)
3070 struct with_triggers_baton_t *b = baton;
3074 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, b->create_trigger));
3076 err1 = b->cb_func(b->cb_baton, wcroot, local_relpath, scratch_pool);
3078 err2 = svn_sqlite__exec_statements(wcroot->sdb, b->drop_trigger);
3080 return svn_error_trace(svn_error_compose_create(err1, err2));
3084 /* Prototype for the "work callback" used by with_finalization(). */
3085 typedef svn_error_t * (*work_callback_t)(
3087 svn_wc__db_wcroot_t *wcroot,
3088 svn_cancel_func_t cancel_func,
3090 svn_wc_notify_func2_t notify_func,
3092 apr_pool_t *scratch_pool);
3094 /* Utility function to provide several features, with a guaranteed
3095 finalization (ie. to drop temporary tables).
3097 1) for WCROOT and LOCAL_RELPATH, run TXN_CB(TXN_BATON) within a
3099 2) if (1) is successful and a NOTIFY_FUNC is provided, then run
3100 the "work" step: WORK_CB(WORK_BATON).
3101 3) execute FINALIZE_STMT_IDX no matter what errors may be thrown
3102 from the above two steps.
3104 CANCEL_FUNC, CANCEL_BATON, NOTIFY_FUNC and NOTIFY_BATON are their
3105 typical values. These are passed to the work callback, which typically
3106 provides notification about the work done by TXN_CB. */
3107 static svn_error_t *
3108 with_finalization(svn_wc__db_wcroot_t *wcroot,
3109 const char *local_relpath,
3110 svn_wc__db_txn_callback_t txn_cb,
3112 work_callback_t work_cb,
3114 svn_cancel_func_t cancel_func,
3116 svn_wc_notify_func2_t notify_func,
3118 int finalize_stmt_idx,
3119 apr_pool_t *scratch_pool)
3124 err1 = svn_sqlite__begin_savepoint(wcroot->sdb);
3127 err1 = txn_cb(txn_baton, wcroot, local_relpath, scratch_pool);
3129 err1 = svn_sqlite__finish_savepoint(wcroot->sdb, err1);
3132 if (err1 == NULL && notify_func != NULL)
3134 err2 = work_cb(work_baton, wcroot,
3135 cancel_func, cancel_baton,
3136 notify_func, notify_baton,
3138 err1 = svn_error_compose_create(err1, err2);
3141 err2 = svn_sqlite__exec_statements(wcroot->sdb, finalize_stmt_idx);
3143 return svn_error_trace(svn_error_compose_create(err1, err2));
3147 /* Initialize the baton with appropriate "blank" values. This allows the
3148 insertion function to leave certain columns null. */
3150 blank_ieb(insert_external_baton_t *ieb)
3152 memset(ieb, 0, sizeof(*ieb));
3153 ieb->revision = SVN_INVALID_REVNUM;
3154 ieb->changed_rev = SVN_INVALID_REVNUM;
3155 ieb->repos_id = INVALID_REPOS_ID;
3157 ieb->recorded_peg_revision = SVN_INVALID_REVNUM;
3158 ieb->recorded_revision = SVN_INVALID_REVNUM;
3161 /* Insert the externals row represented by (insert_external_baton_t *) BATON.
3163 * Implements svn_wc__db_txn_callback_t. */
3164 static svn_error_t *
3165 insert_external_node(const insert_external_baton_t *ieb,
3166 svn_wc__db_wcroot_t *wcroot,
3167 const char *local_relpath,
3168 apr_pool_t *scratch_pool)
3170 svn_wc__db_status_t status;
3172 svn_boolean_t update_root;
3173 apr_int64_t repos_id;
3174 svn_sqlite__stmt_t *stmt;
3176 if (ieb->repos_id != INVALID_REPOS_ID)
3177 repos_id = ieb->repos_id;
3179 SVN_ERR(create_repos_id(&repos_id, ieb->repos_root_url, ieb->repos_uuid,
3180 wcroot->sdb, scratch_pool));
3182 /* And there must be no existing BASE node or it must be a file external */
3183 err = svn_wc__db_base_get_info_internal(&status, NULL, NULL, NULL, NULL,
3184 NULL, NULL, NULL, NULL, NULL,
3185 NULL, NULL, NULL, NULL, &update_root,
3186 wcroot, local_relpath,
3187 scratch_pool, scratch_pool);
3190 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3191 return svn_error_trace(err);
3193 svn_error_clear(err);
3195 else if (status == svn_wc__db_status_normal && !update_root)
3196 return svn_error_create(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, NULL);
3198 if (ieb->kind == svn_node_file
3199 || ieb->kind == svn_node_symlink)
3201 struct insert_base_baton_t ibb;
3205 ibb.status = svn_wc__db_status_normal;
3206 ibb.kind = ieb->kind;
3208 ibb.repos_id = repos_id;
3209 ibb.repos_relpath = ieb->repos_relpath;
3210 ibb.revision = ieb->revision;
3212 ibb.props = ieb->props;
3213 ibb.iprops = ieb->iprops;
3214 ibb.changed_rev = ieb->changed_rev;
3215 ibb.changed_date = ieb->changed_date;
3216 ibb.changed_author = ieb->changed_author;
3218 ibb.dav_cache = ieb->dav_cache;
3220 ibb.checksum = ieb->checksum;
3221 ibb.target = ieb->target;
3223 ibb.conflict = ieb->conflict;
3225 ibb.update_actual_props = ieb->update_actual_props;
3226 ibb.new_actual_props = ieb->new_actual_props;
3228 ibb.keep_recorded_info = ieb->keep_recorded_info;
3230 ibb.work_items = ieb->work_items;
3232 ibb.file_external = TRUE;
3234 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
3237 SVN_ERR(add_work_items(wcroot->sdb, ieb->work_items, scratch_pool));
3239 /* The externals table only support presence normal and excluded */
3240 SVN_ERR_ASSERT(ieb->presence == svn_wc__db_status_normal
3241 || ieb->presence == svn_wc__db_status_excluded);
3243 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_EXTERNAL));
3245 SVN_ERR(svn_sqlite__bindf(stmt, "issttsis",
3248 svn_relpath_dirname(local_relpath,
3250 presence_map, ieb->presence,
3251 kind_map, ieb->kind,
3252 ieb->record_ancestor_relpath,
3254 ieb->recorded_repos_relpath));
3256 if (SVN_IS_VALID_REVNUM(ieb->recorded_peg_revision))
3257 SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, ieb->recorded_peg_revision));
3259 if (SVN_IS_VALID_REVNUM(ieb->recorded_revision))
3260 SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, ieb->recorded_revision));
3262 SVN_ERR(svn_sqlite__insert(NULL, stmt));
3264 return SVN_NO_ERROR;
3268 svn_wc__db_external_add_file(svn_wc__db_t *db,
3269 const char *local_abspath,
3270 const char *wri_abspath,
3272 const char *repos_relpath,
3273 const char *repos_root_url,
3274 const char *repos_uuid,
3275 svn_revnum_t revision,
3277 const apr_hash_t *props,
3278 apr_array_header_t *iprops,
3280 svn_revnum_t changed_rev,
3281 apr_time_t changed_date,
3282 const char *changed_author,
3284 const svn_checksum_t *checksum,
3286 const apr_hash_t *dav_cache,
3288 const char *record_ancestor_abspath,
3289 const char *recorded_repos_relpath,
3290 svn_revnum_t recorded_peg_revision,
3291 svn_revnum_t recorded_revision,
3293 svn_boolean_t update_actual_props,
3294 apr_hash_t *new_actual_props,
3296 svn_boolean_t keep_recorded_info,
3297 const svn_skel_t *conflict,
3298 const svn_skel_t *work_items,
3299 apr_pool_t *scratch_pool)
3301 svn_wc__db_wcroot_t *wcroot;
3302 const char *local_relpath;
3303 insert_external_baton_t ieb;
3305 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3308 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3310 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3311 wri_abspath, scratch_pool, scratch_pool));
3312 VERIFY_USABLE_WCROOT(wcroot);
3314 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3315 record_ancestor_abspath));
3317 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3319 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3323 ieb.kind = svn_node_file;
3324 ieb.presence = svn_wc__db_status_normal;
3326 ieb.repos_root_url = repos_root_url;
3327 ieb.repos_uuid = repos_uuid;
3329 ieb.repos_relpath = repos_relpath;
3330 ieb.revision = revision;
3333 ieb.iprops = iprops;
3335 ieb.changed_rev = changed_rev;
3336 ieb.changed_date = changed_date;
3337 ieb.changed_author = changed_author;
3339 ieb.checksum = checksum;
3341 ieb.dav_cache = dav_cache;
3343 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3345 record_ancestor_abspath);
3346 ieb.recorded_repos_relpath = recorded_repos_relpath;
3347 ieb.recorded_peg_revision = recorded_peg_revision;
3348 ieb.recorded_revision = recorded_revision;
3350 ieb.update_actual_props = update_actual_props;
3351 ieb.new_actual_props = new_actual_props;
3353 ieb.keep_recorded_info = keep_recorded_info;
3355 ieb.conflict = conflict;
3356 ieb.work_items = work_items;
3358 SVN_WC__DB_WITH_TXN(
3359 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3362 return SVN_NO_ERROR;
3366 svn_wc__db_external_add_symlink(svn_wc__db_t *db,
3367 const char *local_abspath,
3368 const char *wri_abspath,
3369 const char *repos_relpath,
3370 const char *repos_root_url,
3371 const char *repos_uuid,
3372 svn_revnum_t revision,
3373 const apr_hash_t *props,
3374 svn_revnum_t changed_rev,
3375 apr_time_t changed_date,
3376 const char *changed_author,
3378 const apr_hash_t *dav_cache,
3379 const char *record_ancestor_abspath,
3380 const char *recorded_repos_relpath,
3381 svn_revnum_t recorded_peg_revision,
3382 svn_revnum_t recorded_revision,
3383 svn_boolean_t update_actual_props,
3384 apr_hash_t *new_actual_props,
3385 svn_boolean_t keep_recorded_info,
3386 const svn_skel_t *work_items,
3387 apr_pool_t *scratch_pool)
3389 svn_wc__db_wcroot_t *wcroot;
3390 const char *local_relpath;
3391 insert_external_baton_t ieb;
3393 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3396 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3398 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3399 wri_abspath, scratch_pool, scratch_pool));
3400 VERIFY_USABLE_WCROOT(wcroot);
3402 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3403 record_ancestor_abspath));
3405 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3407 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3411 ieb.kind = svn_node_symlink;
3412 ieb.presence = svn_wc__db_status_normal;
3414 ieb.repos_root_url = repos_root_url;
3415 ieb.repos_uuid = repos_uuid;
3417 ieb.repos_relpath = repos_relpath;
3418 ieb.revision = revision;
3422 ieb.changed_rev = changed_rev;
3423 ieb.changed_date = changed_date;
3424 ieb.changed_author = changed_author;
3426 ieb.target = target;
3428 ieb.dav_cache = dav_cache;
3430 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3432 record_ancestor_abspath);
3433 ieb.recorded_repos_relpath = recorded_repos_relpath;
3434 ieb.recorded_peg_revision = recorded_peg_revision;
3435 ieb.recorded_revision = recorded_revision;
3437 ieb.update_actual_props = update_actual_props;
3438 ieb.new_actual_props = new_actual_props;
3440 ieb.keep_recorded_info = keep_recorded_info;
3442 ieb.work_items = work_items;
3444 SVN_WC__DB_WITH_TXN(
3445 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3448 return SVN_NO_ERROR;
3452 svn_wc__db_external_add_dir(svn_wc__db_t *db,
3453 const char *local_abspath,
3454 const char *wri_abspath,
3455 const char *repos_root_url,
3456 const char *repos_uuid,
3457 const char *record_ancestor_abspath,
3458 const char *recorded_repos_relpath,
3459 svn_revnum_t recorded_peg_revision,
3460 svn_revnum_t recorded_revision,
3461 const svn_skel_t *work_items,
3462 apr_pool_t *scratch_pool)
3464 svn_wc__db_wcroot_t *wcroot;
3465 const char *local_relpath;
3466 insert_external_baton_t ieb;
3468 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3471 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3473 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3474 wri_abspath, scratch_pool, scratch_pool));
3475 VERIFY_USABLE_WCROOT(wcroot);
3477 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3478 record_ancestor_abspath));
3480 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3482 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3486 ieb.kind = svn_node_dir;
3487 ieb.presence = svn_wc__db_status_normal;
3489 ieb.repos_root_url = repos_root_url;
3490 ieb.repos_uuid = repos_uuid;
3492 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3494 record_ancestor_abspath);
3495 ieb.recorded_repos_relpath = recorded_repos_relpath;
3496 ieb.recorded_peg_revision = recorded_peg_revision;
3497 ieb.recorded_revision = recorded_revision;
3499 ieb.work_items = work_items;
3501 SVN_WC__DB_WITH_TXN(
3502 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3505 return SVN_NO_ERROR;
3508 /* The body of svn_wc__db_external_remove(). */
3509 static svn_error_t *
3510 db_external_remove(const svn_skel_t *work_items,
3511 svn_wc__db_wcroot_t *wcroot,
3512 const char *local_relpath,
3513 apr_pool_t *scratch_pool)
3515 svn_sqlite__stmt_t *stmt;
3518 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3519 STMT_DELETE_EXTERNAL));
3520 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3521 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
3524 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3525 _("The node '%s' is not an external."),
3526 path_for_error_message(wcroot, local_relpath,
3529 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
3531 /* ### What about actual? */
3532 return SVN_NO_ERROR;
3536 svn_wc__db_external_remove(svn_wc__db_t *db,
3537 const char *local_abspath,
3538 const char *wri_abspath,
3539 const svn_skel_t *work_items,
3540 apr_pool_t *scratch_pool)
3542 svn_wc__db_wcroot_t *wcroot;
3543 const char *local_relpath;
3545 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3548 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3550 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3551 wri_abspath, scratch_pool, scratch_pool));
3552 VERIFY_USABLE_WCROOT(wcroot);
3554 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3556 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3558 SVN_WC__DB_WITH_TXN(db_external_remove(work_items, wcroot, local_relpath,
3562 return SVN_NO_ERROR;
3566 svn_wc__db_external_read(svn_wc__db_status_t *status,
3567 svn_node_kind_t *kind,
3568 const char **definining_abspath,
3569 const char **repos_root_url,
3570 const char **repos_uuid,
3571 const char **recorded_repos_relpath,
3572 svn_revnum_t *recorded_peg_revision,
3573 svn_revnum_t *recorded_revision,
3575 const char *local_abspath,
3576 const char *wri_abspath,
3577 apr_pool_t *result_pool,
3578 apr_pool_t *scratch_pool)
3580 svn_wc__db_wcroot_t *wcroot;
3581 const char *local_relpath;
3582 svn_sqlite__stmt_t *stmt;
3583 svn_boolean_t have_info;
3584 svn_error_t *err = NULL;
3585 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3588 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3590 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3591 wri_abspath, scratch_pool, scratch_pool));
3592 VERIFY_USABLE_WCROOT(wcroot);
3594 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3596 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3598 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3599 STMT_SELECT_EXTERNAL_INFO));
3600 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3601 SVN_ERR(svn_sqlite__step(&have_info, stmt));
3606 *status = svn_sqlite__column_token(stmt, 0, presence_map);
3609 *kind = svn_sqlite__column_token(stmt, 1, kind_map);
3611 if (definining_abspath)
3613 const char *record_relpath = svn_sqlite__column_text(stmt, 2, NULL);
3615 *definining_abspath = svn_dirent_join(wcroot->abspath,
3616 record_relpath, result_pool);
3619 if (repos_root_url || repos_uuid)
3621 apr_int64_t repos_id;
3623 repos_id = svn_sqlite__column_int64(stmt, 3);
3625 err = svn_error_compose_create(
3627 svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
3632 if (recorded_repos_relpath)
3633 *recorded_repos_relpath = svn_sqlite__column_text(stmt, 4,
3636 if (recorded_peg_revision)
3637 *recorded_peg_revision = svn_sqlite__column_revnum(stmt, 5);
3639 if (recorded_revision)
3640 *recorded_revision = svn_sqlite__column_revnum(stmt, 6);
3644 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3645 _("The node '%s' is not an external."),
3646 svn_dirent_local_style(local_abspath,
3650 return svn_error_trace(
3651 svn_error_compose_create(err, svn_sqlite__reset(stmt)));
3655 svn_wc__db_committable_externals_below(apr_array_header_t **externals,
3657 const char *local_abspath,
3658 svn_boolean_t immediates_only,
3659 apr_pool_t *result_pool,
3660 apr_pool_t *scratch_pool)
3662 svn_wc__db_wcroot_t *wcroot;
3663 svn_sqlite__stmt_t *stmt;
3664 const char *local_relpath;
3665 svn_boolean_t have_row;
3666 svn_wc__committable_external_info_t *info;
3667 svn_node_kind_t db_kind;
3668 apr_array_header_t *result = NULL;
3670 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3672 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3673 local_abspath, scratch_pool, scratch_pool));
3674 VERIFY_USABLE_WCROOT(wcroot);
3676 SVN_ERR(svn_sqlite__get_statement(
3679 ? STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW
3680 : STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW));
3682 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3684 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3687 result = apr_array_make(result_pool, 0,
3688 sizeof(svn_wc__committable_external_info_t *));
3692 info = apr_palloc(result_pool, sizeof(*info));
3694 local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3695 info->local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
3698 db_kind = svn_sqlite__column_token(stmt, 1, kind_map);
3699 SVN_ERR_ASSERT(db_kind == svn_node_file || db_kind == svn_node_dir);
3700 info->kind = db_kind;
3702 info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
3703 info->repos_root_url = svn_sqlite__column_text(stmt, 3, result_pool);
3705 APR_ARRAY_PUSH(result, svn_wc__committable_external_info_t *) = info;
3707 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3710 *externals = result;
3711 return svn_error_trace(svn_sqlite__reset(stmt));
3715 svn_wc__db_externals_defined_below(apr_hash_t **externals,
3717 const char *local_abspath,
3718 apr_pool_t *result_pool,
3719 apr_pool_t *scratch_pool)
3721 svn_wc__db_wcroot_t *wcroot;
3722 svn_sqlite__stmt_t *stmt;
3723 const char *local_relpath;
3724 svn_boolean_t have_row;
3726 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3728 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3729 local_abspath, scratch_pool, scratch_pool));
3730 VERIFY_USABLE_WCROOT(wcroot);
3732 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3733 STMT_SELECT_EXTERNALS_DEFINED));
3735 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3737 *externals = apr_hash_make(result_pool);
3738 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3742 const char *def_local_relpath;
3744 local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3745 def_local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3747 svn_hash_sets(*externals,
3748 svn_dirent_join(wcroot->abspath, local_relpath,
3750 svn_dirent_join(wcroot->abspath, def_local_relpath,
3753 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3756 return svn_error_trace(svn_sqlite__reset(stmt));
3760 svn_wc__db_externals_gather_definitions(apr_hash_t **externals,
3761 apr_hash_t **depths,
3763 const char *local_abspath,
3764 apr_pool_t *result_pool,
3765 apr_pool_t *scratch_pool)
3767 svn_wc__db_wcroot_t *wcroot;
3768 svn_sqlite__stmt_t *stmt;
3769 const char *local_relpath;
3770 svn_boolean_t have_row;
3771 svn_error_t *err = NULL;
3772 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3774 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3776 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3777 local_abspath, scratch_pool, iterpool));
3778 VERIFY_USABLE_WCROOT(wcroot);
3780 *externals = apr_hash_make(result_pool);
3782 *depths = apr_hash_make(result_pool);
3784 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3785 STMT_SELECT_EXTERNAL_PROPERTIES));
3787 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3789 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3793 apr_hash_t *node_props;
3794 const char *external_value;
3796 svn_pool_clear(iterpool);
3797 err = svn_sqlite__column_properties(&node_props, stmt, 0, iterpool,
3803 external_value = svn_prop_get_value(node_props, SVN_PROP_EXTERNALS);
3807 const char *node_abspath;
3808 const char *node_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3810 node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
3813 svn_hash_sets(*externals, node_abspath,
3814 apr_pstrdup(result_pool, external_value));
3819 = svn_sqlite__column_token_null(stmt, 2, depth_map,
3822 svn_hash_sets(*depths, node_abspath,
3823 /* Use static string */
3824 svn_token__to_word(depth_map, depth));
3828 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3831 svn_pool_destroy(iterpool);
3833 return svn_error_trace(svn_error_compose_create(err,
3834 svn_sqlite__reset(stmt)));
3837 /* Copy the ACTUAL data for SRC_RELPATH and tweak it to refer to DST_RELPATH.
3838 The new ACTUAL data won't have any conflicts. */
3839 static svn_error_t *
3840 copy_actual(svn_wc__db_wcroot_t *src_wcroot,
3841 const char *src_relpath,
3842 svn_wc__db_wcroot_t *dst_wcroot,
3843 const char *dst_relpath,
3844 apr_pool_t *scratch_pool)
3846 svn_sqlite__stmt_t *stmt;
3847 svn_boolean_t have_row;
3849 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
3850 STMT_SELECT_ACTUAL_NODE));
3851 SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
3852 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3855 apr_size_t props_size;
3856 const char *changelist;
3857 const char *properties;
3859 /* Skipping conflict data... */
3860 changelist = svn_sqlite__column_text(stmt, 0, scratch_pool);
3861 /* No need to parse the properties when simply copying. */
3862 properties = svn_sqlite__column_blob(stmt, 1, &props_size, scratch_pool);
3864 if (changelist || properties)
3866 SVN_ERR(svn_sqlite__reset(stmt));
3868 SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
3869 STMT_INSERT_ACTUAL_NODE));
3870 SVN_ERR(svn_sqlite__bindf(stmt, "issbs",
3871 dst_wcroot->wc_id, dst_relpath,
3872 svn_relpath_dirname(dst_relpath, scratch_pool),
3873 properties, props_size, changelist));
3874 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3877 SVN_ERR(svn_sqlite__reset(stmt));
3879 return SVN_NO_ERROR;
3882 /* Helper for svn_wc__db_op_copy to handle copying from one db to
3884 static svn_error_t *
3885 cross_db_copy(svn_wc__db_wcroot_t *src_wcroot,
3886 const char *src_relpath,
3887 svn_wc__db_wcroot_t *dst_wcroot,
3888 const char *dst_relpath,
3889 svn_wc__db_status_t dst_status,
3891 int dst_np_op_depth,
3892 svn_node_kind_t kind,
3893 const apr_array_header_t *children,
3894 apr_int64_t copyfrom_id,
3895 const char *copyfrom_relpath,
3896 svn_revnum_t copyfrom_rev,
3897 apr_pool_t *scratch_pool)
3899 insert_working_baton_t iwb;
3900 svn_revnum_t changed_rev;
3901 apr_time_t changed_date;
3902 const char *changed_author;
3903 const svn_checksum_t *checksum;
3907 SVN_ERR_ASSERT(kind == svn_node_file
3908 || kind == svn_node_dir
3911 SVN_ERR(read_info(NULL, NULL, NULL, NULL, NULL,
3912 &changed_rev, &changed_date, &changed_author, &depth,
3913 &checksum, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3914 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3915 src_wcroot, src_relpath, scratch_pool, scratch_pool));
3917 if (dst_status != svn_wc__db_status_not_present
3918 && dst_status != svn_wc__db_status_excluded
3919 && dst_status != svn_wc__db_status_server_excluded)
3921 SVN_ERR(db_read_pristine_props(&props, src_wcroot, src_relpath, FALSE,
3922 scratch_pool, scratch_pool));
3928 iwb.presence = dst_status;
3932 iwb.changed_rev = changed_rev;
3933 iwb.changed_date = changed_date;
3934 iwb.changed_author = changed_author;
3935 iwb.original_repos_id = copyfrom_id;
3936 iwb.original_repos_relpath = copyfrom_relpath;
3937 iwb.original_revnum = copyfrom_rev;
3938 iwb.moved_here = FALSE;
3940 iwb.op_depth = dst_op_depth;
3942 iwb.checksum = checksum;
3943 iwb.children = children;
3946 iwb.not_present_op_depth = dst_np_op_depth;
3948 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath, scratch_pool));
3950 SVN_ERR(copy_actual(src_wcroot, src_relpath,
3951 dst_wcroot, dst_relpath, scratch_pool));
3953 return SVN_NO_ERROR;
3956 /* Helper for scan_deletion_txn. Extracts the moved-to information, if
3957 any, from STMT. Sets *SCAN to FALSE if moved-to was available. */
3958 static svn_error_t *
3959 get_moved_to(const char **moved_to_relpath_p,
3960 const char **moved_to_op_root_relpath_p,
3961 svn_boolean_t *scan,
3962 svn_sqlite__stmt_t *stmt,
3963 const char *current_relpath,
3964 svn_wc__db_wcroot_t *wcroot,
3965 const char *local_relpath,
3966 apr_pool_t *result_pool,
3967 apr_pool_t *scratch_pool)
3969 const char *moved_to_relpath = svn_sqlite__column_text(stmt, 3, NULL);
3971 if (moved_to_relpath)
3973 const char *moved_to_op_root_relpath = moved_to_relpath;
3975 if (strcmp(current_relpath, local_relpath))
3977 /* LOCAL_RELPATH is a child inside the move op-root. */
3978 const char *moved_child_relpath;
3980 /* The CURRENT_RELPATH is the op_root of the delete-half of
3981 * the move. LOCAL_RELPATH is a child that was moved along.
3982 * Compute the child's new location within the move target. */
3983 moved_child_relpath = svn_relpath_skip_ancestor(current_relpath,
3985 SVN_ERR_ASSERT(moved_child_relpath &&
3986 strlen(moved_child_relpath) > 0);
3987 moved_to_relpath = svn_relpath_join(moved_to_op_root_relpath,
3988 moved_child_relpath,
3992 if (moved_to_op_root_relpath && moved_to_op_root_relpath_p)
3993 *moved_to_op_root_relpath_p
3994 = apr_pstrdup(result_pool, moved_to_op_root_relpath);
3996 if (moved_to_relpath && moved_to_relpath_p)
3998 = apr_pstrdup(result_pool, moved_to_relpath);
4003 return SVN_NO_ERROR;
4007 /* The body of svn_wc__db_scan_deletion().
4009 static svn_error_t *
4010 scan_deletion(const char **base_del_relpath,
4011 const char **moved_to_relpath,
4012 const char **work_del_relpath,
4013 const char **moved_to_op_root_relpath,
4014 svn_wc__db_wcroot_t *wcroot,
4015 const char *local_relpath,
4016 apr_pool_t *result_pool,
4017 apr_pool_t *scratch_pool)
4019 const char *current_relpath = local_relpath;
4020 svn_sqlite__stmt_t *stmt;
4021 svn_wc__db_status_t work_presence;
4022 svn_boolean_t have_row, scan, have_base;
4025 /* Initialize all the OUT parameters. */
4026 if (base_del_relpath != NULL)
4027 *base_del_relpath = NULL;
4028 if (moved_to_relpath != NULL)
4029 *moved_to_relpath = NULL;
4030 if (work_del_relpath != NULL)
4031 *work_del_relpath = NULL;
4032 if (moved_to_op_root_relpath != NULL)
4033 *moved_to_op_root_relpath = NULL;
4035 /* If looking for moved-to info then we need to scan every path
4036 until we find it. If not looking for moved-to we only need to
4037 check op-roots and parents of op-roots. */
4038 scan = (moved_to_op_root_relpath || moved_to_relpath);
4040 SVN_ERR(svn_sqlite__get_statement(
4041 &stmt, wcroot->sdb, STMT_SELECT_DELETION_INFO));
4043 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
4044 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4046 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
4047 _("The node '%s' was not found."),
4048 path_for_error_message(wcroot, local_relpath,
4051 work_presence = svn_sqlite__column_token(stmt, 1, presence_map);
4052 have_base = !svn_sqlite__column_is_null(stmt, 0);
4053 if (work_presence != svn_wc__db_status_not_present
4054 && work_presence != svn_wc__db_status_base_deleted)
4055 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
4056 svn_sqlite__reset(stmt),
4057 _("Expected node '%s' to be deleted."),
4058 path_for_error_message(wcroot, local_relpath,
4061 op_depth = svn_sqlite__column_int(stmt, 2);
4063 /* Special case: LOCAL_RELPATH not-present within a WORKING tree, we
4064 treat this as an op-root. At commit time we need to explicitly
4065 delete such nodes otherwise they will be present in the
4067 if (work_presence == svn_wc__db_status_not_present
4068 && work_del_relpath && !*work_del_relpath)
4070 *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
4072 if (!scan && !base_del_relpath)
4074 /* We have all we need, exit early */
4075 SVN_ERR(svn_sqlite__reset(stmt));
4076 return SVN_NO_ERROR;
4084 const char *parent_relpath;
4085 int current_depth = relpath_depth(current_relpath);
4087 /* Step CURRENT_RELPATH to op-root */
4093 err = get_moved_to(moved_to_relpath, moved_to_op_root_relpath,
4094 &scan, stmt, current_relpath,
4095 wcroot, local_relpath,
4096 result_pool, scratch_pool);
4098 && !base_del_relpath
4099 && !work_del_relpath))
4101 /* We have all we need (or an error occurred) */
4102 SVN_ERR(svn_sqlite__reset(stmt));
4103 return svn_error_trace(err);
4107 if (current_depth <= op_depth)
4110 current_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4113 if (scan || current_depth == op_depth)
4115 SVN_ERR(svn_sqlite__reset(stmt));
4116 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
4118 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4119 SVN_ERR_ASSERT(have_row);
4120 have_base = !svn_sqlite__column_is_null(stmt, 0);
4123 SVN_ERR(svn_sqlite__reset(stmt));
4125 /* Now CURRENT_RELPATH is an op-root, have a look at the parent. */
4127 SVN_ERR_ASSERT(current_relpath[0] != '\0'); /* Catch invalid data */
4128 parent_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4129 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4130 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4133 /* No row means no WORKING node which mean we just fell off
4134 the WORKING tree, so CURRENT_RELPATH is the op-root
4135 closest to the wc root. */
4136 if (have_base && base_del_relpath)
4137 *base_del_relpath = apr_pstrdup(result_pool, current_relpath);
4141 /* Still in the WORKING tree so the first time we get here
4142 CURRENT_RELPATH is a delete op-root in the WORKING tree. */
4143 if (work_del_relpath && !*work_del_relpath)
4145 *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
4147 if (!scan && !base_del_relpath)
4148 break; /* We have all we need */
4151 current_relpath = parent_relpath;
4152 op_depth = svn_sqlite__column_int(stmt, 2);
4153 have_base = !svn_sqlite__column_is_null(stmt, 0);
4156 SVN_ERR(svn_sqlite__reset(stmt));
4158 return SVN_NO_ERROR;
4162 svn_wc__db_scan_deletion_internal(
4163 const char **base_del_relpath,
4164 const char **moved_to_relpath,
4165 const char **work_del_relpath,
4166 const char **moved_to_op_root_relpath,
4167 svn_wc__db_wcroot_t *wcroot,
4168 const char *local_relpath,
4169 apr_pool_t *result_pool,
4170 apr_pool_t *scratch_pool)
4172 return svn_error_trace(
4173 scan_deletion(base_del_relpath, moved_to_relpath, work_del_relpath,
4174 moved_to_op_root_relpath,
4175 wcroot, local_relpath,
4176 result_pool, scratch_pool));
4181 svn_wc__db_scan_deletion(const char **base_del_abspath,
4182 const char **moved_to_abspath,
4183 const char **work_del_abspath,
4184 const char **moved_to_op_root_abspath,
4186 const char *local_abspath,
4187 apr_pool_t *result_pool,
4188 apr_pool_t *scratch_pool)
4190 svn_wc__db_wcroot_t *wcroot;
4191 const char *local_relpath;
4192 const char *base_del_relpath, *moved_to_relpath, *work_del_relpath;
4193 const char *moved_to_op_root_relpath;
4195 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4197 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4198 local_abspath, scratch_pool, scratch_pool));
4199 VERIFY_USABLE_WCROOT(wcroot);
4201 SVN_WC__DB_WITH_TXN(
4202 scan_deletion(&base_del_relpath, &moved_to_relpath,
4203 &work_del_relpath, &moved_to_op_root_relpath,
4204 wcroot, local_relpath, result_pool, scratch_pool),
4207 if (base_del_abspath)
4209 *base_del_abspath = (base_del_relpath
4210 ? svn_dirent_join(wcroot->abspath,
4211 base_del_relpath, result_pool)
4214 if (moved_to_abspath)
4216 *moved_to_abspath = (moved_to_relpath
4217 ? svn_dirent_join(wcroot->abspath,
4218 moved_to_relpath, result_pool)
4221 if (work_del_abspath)
4223 *work_del_abspath = (work_del_relpath
4224 ? svn_dirent_join(wcroot->abspath,
4225 work_del_relpath, result_pool)
4228 if (moved_to_op_root_abspath)
4230 *moved_to_op_root_abspath = (moved_to_op_root_relpath
4231 ? svn_dirent_join(wcroot->abspath,
4232 moved_to_op_root_relpath,
4237 return SVN_NO_ERROR;
4241 /* Set *COPYFROM_ID, *COPYFROM_RELPATH, *COPYFROM_REV to the values
4242 appropriate for the copy. Also return *STATUS, *KIND and *HAVE_WORK, *OP_ROOT
4243 since they are available. This is a helper for
4244 svn_wc__db_op_copy. */
4245 static svn_error_t *
4246 get_info_for_copy(apr_int64_t *copyfrom_id,
4247 const char **copyfrom_relpath,
4248 svn_revnum_t *copyfrom_rev,
4249 svn_wc__db_status_t *status,
4250 svn_node_kind_t *kind,
4251 svn_boolean_t *op_root,
4252 svn_wc__db_wcroot_t *src_wcroot,
4253 const char *local_relpath,
4254 svn_wc__db_wcroot_t *dst_wcroot,
4255 apr_pool_t *result_pool,
4256 apr_pool_t *scratch_pool)
4258 const char *repos_relpath;
4259 svn_revnum_t revision;
4260 svn_wc__db_status_t node_status;
4261 apr_int64_t repos_id;
4262 svn_boolean_t is_op_root;
4264 SVN_ERR(read_info(&node_status, kind, &revision, &repos_relpath, &repos_id,
4265 NULL, NULL, NULL, NULL, NULL, NULL, copyfrom_relpath,
4266 copyfrom_id, copyfrom_rev, NULL, NULL, NULL, NULL,
4267 NULL, &is_op_root, NULL, NULL,
4268 NULL /* have_base */,
4269 NULL /* have_more_work */,
4270 NULL /* have_work */,
4271 src_wcroot, local_relpath, result_pool, scratch_pool));
4274 *op_root = is_op_root;
4276 if (node_status == svn_wc__db_status_excluded)
4278 /* The parent cannot be excluded, so look at the parent and then
4279 adjust the relpath */
4280 const char *parent_relpath, *base_name;
4282 svn_dirent_split(&parent_relpath, &base_name, local_relpath,
4284 SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev,
4286 src_wcroot, parent_relpath, dst_wcroot,
4287 scratch_pool, scratch_pool));
4288 if (*copyfrom_relpath)
4289 *copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, base_name,
4292 else if (node_status == svn_wc__db_status_added)
4294 SVN_ERR(scan_addition(&node_status, NULL, NULL, NULL, NULL, NULL, NULL,
4295 NULL, NULL, NULL, src_wcroot, local_relpath,
4296 scratch_pool, scratch_pool));
4298 else if (node_status == svn_wc__db_status_deleted && is_op_root)
4300 const char *base_del_relpath, *work_del_relpath;
4302 SVN_ERR(scan_deletion(&base_del_relpath, NULL,
4304 NULL, src_wcroot, local_relpath,
4305 scratch_pool, scratch_pool));
4306 if (work_del_relpath)
4308 const char *op_root_relpath;
4309 const char *parent_del_relpath = svn_relpath_dirname(work_del_relpath,
4312 /* Similar to, but not the same as, the _scan_addition and
4313 _join above. Can we use get_copyfrom here? */
4314 SVN_ERR(scan_addition(NULL, &op_root_relpath,
4315 NULL, NULL, /* repos_* */
4316 copyfrom_relpath, copyfrom_id, copyfrom_rev,
4318 src_wcroot, parent_del_relpath,
4319 scratch_pool, scratch_pool));
4321 = svn_relpath_join(*copyfrom_relpath,
4322 svn_relpath_skip_ancestor(op_root_relpath,
4326 else if (base_del_relpath)
4328 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, copyfrom_rev,
4330 copyfrom_id, NULL, NULL,
4331 NULL, NULL, NULL, NULL,
4332 NULL, NULL, NULL, NULL,
4333 src_wcroot, local_relpath,
4338 SVN_ERR_MALFUNCTION();
4340 else if (node_status == svn_wc__db_status_deleted)
4342 /* Keep original_* from read_info() to allow seeing the difference
4343 between base-deleted and not present */
4347 *copyfrom_relpath = repos_relpath;
4348 *copyfrom_rev = revision;
4349 *copyfrom_id = repos_id;
4353 *status = node_status;
4355 if (src_wcroot != dst_wcroot && *copyfrom_relpath)
4357 const char *repos_root_url;
4358 const char *repos_uuid;
4360 /* Pass the right repos-id for the destination db. We can't just use
4361 the id of the source database, as this value can change after
4362 relocation (and perhaps also when we start storing multiple
4363 working copies in a single db)! */
4365 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
4366 src_wcroot, *copyfrom_id,
4369 SVN_ERR(create_repos_id(copyfrom_id, repos_root_url, repos_uuid,
4370 dst_wcroot->sdb, scratch_pool));
4373 return SVN_NO_ERROR;
4377 /* Set *OP_DEPTH to the highest op depth of WCROOT:LOCAL_RELPATH. */
4378 static svn_error_t *
4379 op_depth_of(int *op_depth,
4380 svn_wc__db_wcroot_t *wcroot,
4381 const char *local_relpath)
4383 svn_sqlite__stmt_t *stmt;
4384 svn_boolean_t have_row;
4386 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4387 STMT_SELECT_NODE_INFO));
4388 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4389 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4390 SVN_ERR_ASSERT(have_row);
4391 *op_depth = svn_sqlite__column_int(stmt, 0);
4392 SVN_ERR(svn_sqlite__reset(stmt));
4394 return SVN_NO_ERROR;
4398 /* Determine at which OP_DEPTH a copy of COPYFROM_REPOS_ID, COPYFROM_RELPATH at
4399 revision COPYFROM_REVISION should be inserted as LOCAL_RELPATH. Do this
4400 by checking if this would be a direct child of a copy of its parent
4401 directory. If it is then set *OP_DEPTH to the op_depth of its parent.
4403 If the node is not a direct copy at the same revision of the parent
4404 *NP_OP_DEPTH will be set to the op_depth of the parent when a not-present
4405 node should be inserted at this op_depth. This will be the case when the
4406 parent already defined an incomplete child with the same name. Otherwise
4407 *NP_OP_DEPTH will be set to -1.
4409 If the parent node is not the parent of the to be copied node, then
4410 *OP_DEPTH will be set to the proper op_depth for a new operation root.
4412 Set *PARENT_OP_DEPTH to the op_depth of the parent.
4415 static svn_error_t *
4416 op_depth_for_copy(int *op_depth,
4418 int *parent_op_depth,
4419 apr_int64_t copyfrom_repos_id,
4420 const char *copyfrom_relpath,
4421 svn_revnum_t copyfrom_revision,
4422 svn_wc__db_wcroot_t *wcroot,
4423 const char *local_relpath,
4424 apr_pool_t *scratch_pool)
4426 const char *parent_relpath, *name;
4427 svn_sqlite__stmt_t *stmt;
4428 svn_boolean_t have_row;
4429 int incomplete_op_depth = -1;
4430 int min_op_depth = 1; /* Never touch BASE */
4432 *op_depth = relpath_depth(local_relpath);
4435 svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool);
4436 *parent_op_depth = relpath_depth(parent_relpath);
4438 if (!copyfrom_relpath)
4439 return SVN_NO_ERROR;
4441 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4442 STMT_SELECT_WORKING_NODE));
4443 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4444 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4447 svn_wc__db_status_t status = svn_sqlite__column_token(stmt, 1,
4450 min_op_depth = svn_sqlite__column_int(stmt, 0);
4451 if (status == svn_wc__db_status_incomplete)
4452 incomplete_op_depth = min_op_depth;
4454 SVN_ERR(svn_sqlite__reset(stmt));
4456 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4457 STMT_SELECT_WORKING_NODE));
4458 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4459 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4462 svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 1,
4465 *parent_op_depth = svn_sqlite__column_int(stmt, 0);
4466 if (*parent_op_depth < min_op_depth)
4468 /* We want to create a copy; not overwrite the lower layers */
4469 SVN_ERR(svn_sqlite__reset(stmt));
4470 return SVN_NO_ERROR;
4473 /* You can only add children below a node that exists.
4474 In WORKING that must be status added, which is represented
4475 as presence normal */
4476 SVN_ERR_ASSERT(presence == svn_wc__db_status_normal);
4478 if ((incomplete_op_depth < 0)
4479 || (incomplete_op_depth == *parent_op_depth))
4481 apr_int64_t parent_copyfrom_repos_id
4482 = svn_sqlite__column_int64(stmt, 10);
4483 const char *parent_copyfrom_relpath
4484 = svn_sqlite__column_text(stmt, 11, NULL);
4485 svn_revnum_t parent_copyfrom_revision
4486 = svn_sqlite__column_revnum(stmt, 12);
4488 if (parent_copyfrom_repos_id == copyfrom_repos_id)
4490 if (copyfrom_revision == parent_copyfrom_revision
4491 && !strcmp(copyfrom_relpath,
4492 svn_relpath_join(parent_copyfrom_relpath, name,
4494 *op_depth = *parent_op_depth;
4495 else if (incomplete_op_depth > 0)
4496 *np_op_depth = incomplete_op_depth;
4500 SVN_ERR(svn_sqlite__reset(stmt));
4502 return SVN_NO_ERROR;
4506 /* Like svn_wc__db_op_copy(), but with WCROOT+LOCAL_RELPATH
4507 * instead of DB+LOCAL_ABSPATH. A non-zero MOVE_OP_DEPTH implies that the
4508 * copy operation is part of a move, and indicates the op-depth of the
4509 * move destination op-root. */
4510 static svn_error_t *
4511 db_op_copy(svn_wc__db_wcroot_t *src_wcroot,
4512 const char *src_relpath,
4513 svn_wc__db_wcroot_t *dst_wcroot,
4514 const char *dst_relpath,
4515 const svn_skel_t *work_items,
4517 apr_pool_t *scratch_pool)
4519 const char *copyfrom_relpath;
4520 svn_revnum_t copyfrom_rev;
4521 svn_wc__db_status_t status;
4522 svn_wc__db_status_t dst_presence;
4523 svn_boolean_t op_root;
4524 apr_int64_t copyfrom_id;
4526 int dst_np_op_depth;
4527 int dst_parent_op_depth;
4528 svn_node_kind_t kind;
4529 const apr_array_header_t *children;
4531 SVN_ERR(get_info_for_copy(©from_id, ©from_relpath, ©from_rev,
4532 &status, &kind, &op_root,
4533 src_wcroot, src_relpath, dst_wcroot,
4534 scratch_pool, scratch_pool));
4536 SVN_ERR(op_depth_for_copy(&dst_op_depth, &dst_np_op_depth,
4537 &dst_parent_op_depth,
4538 copyfrom_id, copyfrom_relpath, copyfrom_rev,
4539 dst_wcroot, dst_relpath, scratch_pool));
4541 SVN_ERR_ASSERT(kind == svn_node_file || kind == svn_node_dir);
4543 /* ### New status, not finished, see notes/wc-ng/copying */
4546 case svn_wc__db_status_normal:
4547 case svn_wc__db_status_added:
4548 case svn_wc__db_status_moved_here:
4549 case svn_wc__db_status_copied:
4550 dst_presence = svn_wc__db_status_normal;
4552 case svn_wc__db_status_deleted:
4555 /* If the lower layer is already shadowcopied we can skip adding
4556 a not present node. */
4558 svn_wc__db_status_t dst_status;
4560 err = read_info(&dst_status, NULL, NULL, NULL, NULL, NULL, NULL,
4561 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4562 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4563 dst_wcroot, dst_relpath, scratch_pool, scratch_pool);
4567 if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
4568 svn_error_clear(err);
4570 return svn_error_trace(err);
4572 else if (dst_status == svn_wc__db_status_deleted)
4574 /* Node is already deleted; skip the NODES work, but do
4575 install wq items if requested */
4576 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4578 return SVN_NO_ERROR;
4583 /* This node is either a not-present node (which should be copied), or
4584 a base-delete of some lower layer (which shouldn't).
4585 Subversion <= 1.7 always added a not-present node here, which is
4586 safe (as it postpones the hard work until commit time and then we
4587 ask the repository), but it breaks some move scenarios.
4590 if (! copyfrom_relpath)
4592 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4594 return SVN_NO_ERROR;
4597 /* Fall through. Install not present node */
4599 case svn_wc__db_status_not_present:
4600 case svn_wc__db_status_excluded:
4601 /* These presence values should not create a new op depth */
4602 if (dst_np_op_depth > 0)
4604 dst_op_depth = dst_np_op_depth;
4605 dst_np_op_depth = -1;
4607 if (status == svn_wc__db_status_excluded)
4608 dst_presence = svn_wc__db_status_excluded;
4610 dst_presence = svn_wc__db_status_not_present;
4612 case svn_wc__db_status_server_excluded:
4613 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4614 _("Cannot copy '%s' excluded by server"),
4615 path_for_error_message(src_wcroot,
4619 /* Perhaps we should allow incomplete to incomplete? We can't
4620 avoid incomplete working nodes as one step in copying a
4621 directory is to add incomplete children. */
4622 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4623 _("Cannot handle status of '%s'"),
4624 path_for_error_message(src_wcroot,
4629 if (kind == svn_node_dir)
4633 SVN_ERR(op_depth_of(&src_op_depth, src_wcroot, src_relpath));
4634 SVN_ERR(gather_children(&children, src_wcroot, src_relpath,
4635 STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth,
4636 scratch_pool, scratch_pool));
4641 if (src_wcroot == dst_wcroot)
4643 svn_sqlite__stmt_t *stmt;
4644 const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
4647 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
4648 STMT_INSERT_WORKING_NODE_COPY_FROM));
4650 SVN_ERR(svn_sqlite__bindf(stmt, "issdst",
4651 src_wcroot->wc_id, src_relpath,
4655 presence_map, dst_presence));
4657 if (move_op_depth > 0)
4659 if (relpath_depth(dst_relpath) == move_op_depth)
4661 /* We're moving the root of the move operation.
4663 * When an added node or the op-root of a copy is moved,
4664 * there is no 'moved-from' corresponding to the moved-here
4665 * node. So the net effect is the same as copy+delete.
4666 * Perform a normal copy operation in these cases. */
4667 if (!(status == svn_wc__db_status_added ||
4668 (status == svn_wc__db_status_copied && op_root)))
4669 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4673 svn_sqlite__stmt_t *info_stmt;
4674 svn_boolean_t have_row;
4676 /* We're moving a child along with the root of the move.
4678 * Set moved-here depending on dst_parent, propagating the
4679 * above decision to moved-along children at the same op_depth.
4680 * We can't use scan_addition() to detect moved-here because
4681 * the delete-half of the move might not yet exist. */
4682 SVN_ERR(svn_sqlite__get_statement(&info_stmt, dst_wcroot->sdb,
4683 STMT_SELECT_NODE_INFO));
4684 SVN_ERR(svn_sqlite__bindf(info_stmt, "is", dst_wcroot->wc_id,
4685 dst_parent_relpath));
4686 SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4687 SVN_ERR_ASSERT(have_row);
4688 if (svn_sqlite__column_boolean(info_stmt, 15) &&
4689 dst_op_depth == dst_parent_op_depth)
4691 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4692 SVN_ERR(svn_sqlite__reset(info_stmt));
4696 SVN_ERR(svn_sqlite__reset(info_stmt));
4698 /* If the child has been moved into the tree we're moving,
4699 * keep its moved-here bit set. */
4700 SVN_ERR(svn_sqlite__get_statement(&info_stmt,
4702 STMT_SELECT_NODE_INFO));
4703 SVN_ERR(svn_sqlite__bindf(info_stmt, "is",
4704 dst_wcroot->wc_id, src_relpath));
4705 SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4706 SVN_ERR_ASSERT(have_row);
4707 if (svn_sqlite__column_boolean(info_stmt, 15))
4708 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4709 SVN_ERR(svn_sqlite__reset(info_stmt));
4714 SVN_ERR(svn_sqlite__step_done(stmt));
4716 /* ### Copying changelist is OK for a move but what about a copy? */
4717 SVN_ERR(copy_actual(src_wcroot, src_relpath,
4718 dst_wcroot, dst_relpath, scratch_pool));
4720 if (dst_np_op_depth > 0)
4722 /* We introduce a not-present node at the parent's op_depth to
4723 properly start a new op-depth at our own op_depth. This marks
4724 us as an op_root for commit and allows reverting just this
4727 SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
4729 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
4730 src_wcroot->wc_id, dst_relpath,
4731 dst_np_op_depth, dst_parent_relpath,
4732 copyfrom_id, copyfrom_relpath,
4735 svn_wc__db_status_not_present,
4739 SVN_ERR(svn_sqlite__step_done(stmt));
4741 /* Insert incomplete children, if relevant.
4742 The children are part of the same op and so have the same op_depth.
4743 (The only time we'd want a different depth is during a recursive
4744 simple add, but we never insert children here during a simple add.) */
4745 if (kind == svn_node_dir
4746 && dst_presence == svn_wc__db_status_normal)
4747 SVN_ERR(insert_incomplete_children(
4760 SVN_ERR(cross_db_copy(src_wcroot, src_relpath, dst_wcroot,
4761 dst_relpath, dst_presence, dst_op_depth,
4762 dst_np_op_depth, kind,
4763 children, copyfrom_id, copyfrom_relpath,
4764 copyfrom_rev, scratch_pool));
4767 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items, scratch_pool));
4769 return SVN_NO_ERROR;
4772 /* Baton for passing args to op_copy_txn(). */
4773 struct op_copy_baton
4775 svn_wc__db_wcroot_t *src_wcroot;
4776 const char *src_relpath;
4778 svn_wc__db_wcroot_t *dst_wcroot;
4779 const char *dst_relpath;
4781 const svn_skel_t *work_items;
4783 svn_boolean_t is_move;
4784 const char *dst_op_root_relpath;
4787 /* Helper for svn_wc__db_op_copy(). */
4788 static svn_error_t *
4789 op_copy_txn(svn_wc__db_wcroot_t *wcroot,
4790 struct op_copy_baton *ocb,
4791 apr_pool_t *scratch_pool)
4795 if (wcroot != ocb->dst_wcroot)
4797 /* Source and destination databases differ; so also start a lock
4798 in the destination database, by calling ourself in an extra lock. */
4800 SVN_WC__DB_WITH_TXN(op_copy_txn(ocb->dst_wcroot, ocb, scratch_pool),
4803 return SVN_NO_ERROR;
4806 /* From this point we can assume a lock in the src and dst databases */
4809 move_op_depth = relpath_depth(ocb->dst_op_root_relpath);
4813 SVN_ERR(db_op_copy(ocb->src_wcroot, ocb->src_relpath,
4814 ocb->dst_wcroot, ocb->dst_relpath,
4815 ocb->work_items, move_op_depth, scratch_pool));
4817 return SVN_NO_ERROR;
4821 svn_wc__db_op_copy(svn_wc__db_t *db,
4822 const char *src_abspath,
4823 const char *dst_abspath,
4824 const char *dst_op_root_abspath,
4825 svn_boolean_t is_move,
4826 const svn_skel_t *work_items,
4827 apr_pool_t *scratch_pool)
4829 struct op_copy_baton ocb = {0};
4831 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
4832 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
4833 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_op_root_abspath));
4835 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
4836 &ocb.src_relpath, db,
4838 scratch_pool, scratch_pool));
4839 VERIFY_USABLE_WCROOT(ocb.src_wcroot);
4841 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
4844 scratch_pool, scratch_pool));
4845 VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
4847 ocb.work_items = work_items;
4848 ocb.is_move = is_move;
4849 ocb.dst_op_root_relpath = svn_dirent_skip_ancestor(ocb.dst_wcroot->abspath,
4850 dst_op_root_abspath);
4852 /* Call with the sdb in src_wcroot. It might call itself again to
4853 also obtain a lock in dst_wcroot */
4854 SVN_WC__DB_WITH_TXN(op_copy_txn(ocb.src_wcroot, &ocb, scratch_pool),
4857 return SVN_NO_ERROR;
4860 /* Remove unneeded actual nodes for svn_wc__db_op_copy_layer_internal */
4861 static svn_error_t *
4862 clear_or_remove_actual(svn_wc__db_wcroot_t *wcroot,
4863 const char *local_relpath,
4865 apr_pool_t *scratch_pool)
4867 svn_sqlite__stmt_t *stmt;
4868 svn_boolean_t have_row, shadowed;
4869 svn_boolean_t keep_conflict = FALSE;
4871 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4872 STMT_SELECT_NODE_INFO));
4874 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4875 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4879 svn_wc__db_status_t presence;
4881 shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth);
4882 presence = svn_sqlite__column_token(stmt, 3, presence_map);
4884 if (shadowed && presence == svn_wc__db_status_base_deleted)
4886 keep_conflict = TRUE;
4887 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4890 shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth);
4898 SVN_ERR(svn_sqlite__reset(stmt));
4900 return SVN_NO_ERROR;
4904 /* We don't want to accidentally remove delete-delete conflicts */
4905 SVN_ERR(svn_sqlite__get_statement(
4907 STMT_CLEAR_ACTUAL_NODE_LEAVING_CONFLICT));
4908 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4909 SVN_ERR(svn_sqlite__step_done(stmt));
4910 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4911 STMT_DELETE_ACTUAL_EMPTY));
4912 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4913 SVN_ERR(svn_sqlite__step_done(stmt));
4917 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4918 STMT_DELETE_ACTUAL_NODE));
4919 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4920 SVN_ERR(svn_sqlite__step_done(stmt));
4923 return SVN_NO_ERROR;
4927 svn_wc__db_op_copy_layer_internal(svn_wc__db_wcroot_t *wcroot,
4928 const char *src_op_relpath,
4930 const char *dst_op_relpath,
4931 svn_skel_t *conflict,
4932 svn_skel_t *work_items,
4933 apr_pool_t *scratch_pool)
4935 svn_sqlite__stmt_t *stmt, *stmt2;
4936 svn_boolean_t have_row;
4937 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4938 int dst_op_depth = relpath_depth(dst_op_relpath);
4939 svn_boolean_t locked;
4940 svn_error_t *err = NULL;
4942 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, dst_op_relpath,
4943 FALSE, scratch_pool));
4946 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
4947 _("No write-lock in '%s'"),
4948 path_for_error_message(wcroot, dst_op_relpath,
4951 SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
4952 STMT_COPY_NODE_MOVE));
4954 /* Replace entire subtree at one op-depth. */
4955 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4956 STMT_SELECT_LAYER_FOR_REPLACE));
4957 SVN_ERR(svn_sqlite__bindf(stmt, "isdsd", wcroot->wc_id,
4958 src_op_relpath, src_op_depth,
4959 dst_op_relpath, dst_op_depth));
4960 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4963 const char *src_relpath;
4964 const char *dst_relpath;
4966 svn_pool_clear(iterpool);
4968 src_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
4969 dst_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
4971 err = svn_sqlite__bindf(stmt2, "isdsds", wcroot->wc_id,
4972 src_relpath, src_op_depth,
4973 dst_relpath, dst_op_depth,
4974 svn_relpath_dirname(dst_relpath, iterpool));
4976 err = svn_sqlite__step_done(stmt2);
4978 /* stmt2 is reset (never modified or by step_done) */
4983 /* The node can't be deleted where it is added, so extension of
4984 an existing shadowing is only interesting 2 levels deep. */
4985 if (relpath_depth(dst_relpath) > (dst_op_depth+1))
4987 svn_boolean_t exists = !svn_sqlite__column_is_null(stmt, 3);
4991 svn_wc__db_status_t presence;
4993 presence = svn_sqlite__column_token(stmt, 3, presence_map);
4995 if (presence != svn_wc__db_status_normal)
5001 svn_node_kind_t kind = svn_sqlite__column_token(stmt, 1, kind_map);
5003 err = db_extend_parent_delete(wcroot, dst_relpath,
5004 kind, dst_op_depth, iterpool);
5011 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5014 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
5016 /* And now remove the records that are no longer needed */
5017 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5018 STMT_SELECT_NO_LONGER_MOVED_RV));
5019 SVN_ERR(svn_sqlite__bindf(stmt, "isdsd", wcroot->wc_id,
5020 dst_op_relpath, dst_op_depth,
5021 src_op_relpath, src_op_depth));
5022 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5025 const char *dst_relpath;
5026 svn_wc__db_status_t shadowed_presence;
5028 svn_pool_clear(iterpool);
5030 dst_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
5032 if (!svn_sqlite__column_is_null(stmt, 2))
5033 shadowed_presence = svn_sqlite__column_token(stmt, 2, presence_map);
5035 shadowed_presence = svn_wc__db_status_not_present;
5037 if (shadowed_presence != svn_wc__db_status_normal
5038 && shadowed_presence != svn_wc__db_status_incomplete)
5040 err = svn_sqlite__get_statement(&stmt2, wcroot->sdb,
5045 err =svn_sqlite__get_statement(&stmt2, wcroot->sdb,
5046 STMT_REPLACE_WITH_BASE_DELETED);
5050 err = svn_sqlite__bindf(stmt2, "isd", wcroot->wc_id, dst_relpath,
5054 err = svn_sqlite__step_done(stmt2);
5056 /* stmt2 is reset (never modified or by step_done) */
5060 /* Delete ACTUAL information about this node that we just deleted */
5061 err = clear_or_remove_actual(wcroot, dst_relpath, dst_op_depth,
5067 /* Retract base-delete for the node itself */
5068 err = db_retract_parent_delete(wcroot, dst_relpath, dst_op_depth,
5074 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5076 svn_pool_destroy(iterpool);
5078 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
5080 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5083 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, dst_op_relpath /* ## */,
5084 conflict, scratch_pool));
5086 return SVN_NO_ERROR;
5089 /* The txn body of svn_wc__db_op_handle_move_back */
5090 static svn_error_t *
5091 handle_move_back(svn_boolean_t *moved_back,
5092 svn_wc__db_wcroot_t *wcroot,
5093 const char *local_relpath,
5094 const char *moved_from_relpath,
5095 const svn_skel_t *work_items,
5096 apr_pool_t *scratch_pool)
5098 svn_sqlite__stmt_t *stmt;
5099 svn_wc__db_status_t status;
5100 svn_boolean_t op_root;
5101 svn_boolean_t have_more_work;
5102 int from_op_depth = 0;
5103 svn_boolean_t have_row;
5104 svn_boolean_t different = FALSE;
5106 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5108 SVN_ERR(svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL, NULL,
5109 NULL, NULL, NULL, NULL, NULL, NULL,
5110 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5111 &op_root, NULL, NULL, NULL,
5112 &have_more_work, NULL,
5113 wcroot, local_relpath,
5114 scratch_pool, scratch_pool));
5116 if (status != svn_wc__db_status_added || !op_root)
5117 return SVN_NO_ERROR;
5119 /* We have two cases here: BASE-move-back and WORKING-move-back */
5121 SVN_ERR(op_depth_of(&from_op_depth, wcroot,
5122 svn_relpath_dirname(local_relpath, scratch_pool)));
5126 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5127 STMT_SELECT_MOVED_BACK));
5129 SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id,
5132 relpath_depth(local_relpath)));
5134 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5136 SVN_ERR_ASSERT(have_row); /* We checked that the node is an op-root */
5139 svn_boolean_t moved_here = svn_sqlite__column_boolean(stmt, 9);
5140 const char *moved_to = svn_sqlite__column_text(stmt, 10, NULL);
5144 || strcmp(moved_to, moved_from_relpath))
5153 svn_wc__db_status_t upper_status;
5154 svn_wc__db_status_t lower_status;
5156 upper_status = svn_sqlite__column_token(stmt, 1, presence_map);
5158 if (svn_sqlite__column_is_null(stmt, 5))
5160 /* No lower layer replaced. */
5161 if (upper_status != svn_wc__db_status_not_present)
5169 lower_status = svn_sqlite__column_token(stmt, 5, presence_map);
5171 if (upper_status != lower_status)
5177 if (upper_status == svn_wc__db_status_not_present
5178 || upper_status == svn_wc__db_status_excluded)
5180 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5181 continue; /* Nothing to check */
5183 else if (upper_status != svn_wc__db_status_normal)
5185 /* Not a normal move. Mixed revision move? */
5191 const char *upper_repos_relpath;
5192 const char *lower_repos_relpath;
5194 upper_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL);
5195 lower_repos_relpath = svn_sqlite__column_text(stmt, 7, NULL);
5197 if (! upper_repos_relpath
5198 || strcmp(upper_repos_relpath, lower_repos_relpath))
5206 svn_revnum_t upper_rev;
5207 svn_revnum_t lower_rev;
5209 upper_rev = svn_sqlite__column_revnum(stmt, 4);
5210 lower_rev = svn_sqlite__column_revnum(stmt, 8);
5212 if (upper_rev != lower_rev)
5220 apr_int64_t upper_repos_id;
5221 apr_int64_t lower_repos_id;
5223 upper_repos_id = svn_sqlite__column_int64(stmt, 2);
5224 lower_repos_id = svn_sqlite__column_int64(stmt, 6);
5226 if (upper_repos_id != lower_repos_id)
5233 /* Check moved_here? */
5235 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5237 SVN_ERR(svn_sqlite__reset(stmt));
5241 /* Ok, we can now safely remove this complete move, because we
5242 determined that it 100% matches the layer below it. */
5244 /* ### We could copy the recorded timestamps from the higher to the
5245 lower layer in an attempt to improve status performance, but
5246 generally these values should be the same anyway as it was
5248 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5249 STMT_DELETE_WORKING_OP_DEPTH));
5251 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
5253 relpath_depth(local_relpath)));
5255 SVN_ERR(svn_sqlite__step_done(stmt));
5261 return SVN_NO_ERROR;
5265 svn_wc__db_op_handle_move_back(svn_boolean_t *moved_back,
5267 const char *local_abspath,
5268 const char *moved_from_abspath,
5269 const svn_skel_t *work_items,
5270 apr_pool_t *scratch_pool)
5272 svn_wc__db_wcroot_t *wcroot;
5273 const char *local_relpath;
5274 const char *moved_from_relpath;
5275 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5277 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5279 scratch_pool, scratch_pool));
5280 VERIFY_USABLE_WCROOT(wcroot);
5283 *moved_back = FALSE;
5285 moved_from_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
5286 moved_from_abspath);
5288 if (! local_relpath[0]
5289 || !moved_from_relpath)
5291 /* WC-Roots can't be moved */
5292 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5293 return SVN_NO_ERROR;
5296 SVN_WC__DB_WITH_TXN(handle_move_back(moved_back, wcroot, local_relpath,
5297 moved_from_relpath, work_items,
5301 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5304 return SVN_NO_ERROR;
5308 /* The recursive implementation of svn_wc__db_op_copy_shadowed_layer.
5310 * A non-zero MOVE_OP_DEPTH implies that the copy operation is part of
5311 * a move, and indicates the op-depth of the move destination op-root. */
5312 static svn_error_t *
5313 db_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot,
5314 const char *src_relpath,
5316 svn_wc__db_wcroot_t *dst_wcroot,
5317 const char *dst_relpath,
5320 apr_int64_t repos_id,
5321 const char *repos_relpath,
5322 svn_revnum_t revision,
5324 apr_pool_t *scratch_pool)
5326 const apr_array_header_t *children;
5327 apr_pool_t *iterpool;
5328 svn_wc__db_status_t status;
5329 svn_node_kind_t kind;
5330 svn_revnum_t node_revision;
5331 const char *node_repos_relpath;
5332 apr_int64_t node_repos_id;
5333 svn_sqlite__stmt_t *stmt;
5334 svn_wc__db_status_t dst_presence;
5339 err = svn_wc__db_depth_get_info(&status, &kind, &node_revision,
5340 &node_repos_relpath, &node_repos_id,
5341 NULL, NULL, NULL, NULL, NULL, NULL,
5343 src_wcroot, src_relpath, src_op_depth,
5344 scratch_pool, scratch_pool);
5348 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5349 return svn_error_trace(err);
5351 svn_error_clear(err);
5352 return SVN_NO_ERROR; /* There is no shadowed node at src_op_depth */
5356 if (src_op_depth == 0)
5358 /* If the node is switched or has a different revision then its parent
5359 we shouldn't copy it. (We can't as we would have to insert it at
5360 an unshadowed depth) */
5361 if (status == svn_wc__db_status_not_present
5362 || status == svn_wc__db_status_excluded
5363 || status == svn_wc__db_status_server_excluded
5364 || node_revision != revision
5365 || node_repos_id != repos_id
5366 || strcmp(node_repos_relpath, repos_relpath))
5368 /* Add a not-present node in the destination wcroot */
5369 struct insert_working_baton_t iwb;
5370 const char *repos_root_url;
5371 const char *repos_uuid;
5373 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
5374 src_wcroot, node_repos_id,
5377 SVN_ERR(create_repos_id(&node_repos_id, repos_root_url, repos_uuid,
5378 dst_wcroot->sdb, scratch_pool));
5382 iwb.op_depth = dst_op_depth;
5383 if (status != svn_wc__db_status_excluded)
5384 iwb.presence = svn_wc__db_status_not_present;
5386 iwb.presence = svn_wc__db_status_excluded;
5390 iwb.original_repos_id = node_repos_id;
5391 iwb.original_revnum = node_revision;
5392 iwb.original_repos_relpath = node_repos_relpath;
5394 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5397 return SVN_NO_ERROR;
5401 iterpool = svn_pool_create(scratch_pool);
5405 case svn_wc__db_status_normal:
5406 case svn_wc__db_status_added:
5407 case svn_wc__db_status_moved_here:
5408 case svn_wc__db_status_copied:
5409 dst_presence = svn_wc__db_status_normal;
5411 case svn_wc__db_status_deleted:
5412 case svn_wc__db_status_not_present:
5413 dst_presence = svn_wc__db_status_not_present;
5415 case svn_wc__db_status_excluded:
5416 dst_presence = svn_wc__db_status_excluded;
5418 case svn_wc__db_status_server_excluded:
5419 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5420 _("Cannot copy '%s' excluded by server"),
5421 path_for_error_message(src_wcroot,
5425 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5426 _("Cannot handle status of '%s'"),
5427 path_for_error_message(src_wcroot,
5432 if (dst_presence == svn_wc__db_status_normal
5433 && src_wcroot == dst_wcroot) /* ### Remove limitation */
5435 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
5436 STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH));
5438 SVN_ERR(svn_sqlite__bindf(stmt, "issdstd",
5439 src_wcroot->wc_id, src_relpath,
5442 svn_relpath_dirname(dst_relpath, iterpool),
5443 presence_map, dst_presence,
5447 if (dst_op_depth == move_op_depth)
5448 SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
5450 SVN_ERR(svn_sqlite__step_done(stmt));
5453 /* And mark it deleted to allow proper shadowing */
5454 struct insert_working_baton_t iwb;
5458 iwb.op_depth = del_op_depth;
5459 iwb.presence = svn_wc__db_status_base_deleted;
5463 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5469 struct insert_working_baton_t iwb;
5470 if (dst_presence == svn_wc__db_status_normal) /* Fallback for multi-db */
5471 dst_presence = svn_wc__db_status_not_present;
5473 /* And mark it deleted to allow proper shadowing */
5477 iwb.op_depth = dst_op_depth;
5478 iwb.presence = dst_presence;
5481 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5485 if (dst_presence == svn_wc__db_status_not_present)
5487 /* Don't create descendants of a not present node! */
5489 /* This code is currently still triggered by copying deleted nodes
5490 between separate working copies. See ### comment above. */
5492 svn_pool_destroy(iterpool);
5493 return SVN_NO_ERROR;
5496 SVN_ERR(gather_children(&children, src_wcroot, src_relpath,
5497 STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth,
5498 scratch_pool, iterpool));
5500 for (i = 0; i < children->nelts; i++)
5502 const char *name = APR_ARRAY_IDX(children, i, const char *);
5503 const char *child_src_relpath;
5504 const char *child_dst_relpath;
5505 const char *child_repos_relpath = NULL;
5507 svn_pool_clear(iterpool);
5508 child_src_relpath = svn_relpath_join(src_relpath, name, iterpool);
5509 child_dst_relpath = svn_relpath_join(dst_relpath, name, iterpool);
5512 child_repos_relpath = svn_relpath_join(repos_relpath, name, iterpool);
5514 SVN_ERR(db_op_copy_shadowed_layer(
5515 src_wcroot, child_src_relpath, src_op_depth,
5516 dst_wcroot, child_dst_relpath, dst_op_depth,
5518 repos_id, child_repos_relpath, revision,
5519 move_op_depth, scratch_pool));
5522 svn_pool_destroy(iterpool);
5524 return SVN_NO_ERROR;
5527 /* Helper for svn_wc__db_op_copy_shadowed_layer(). */
5528 static svn_error_t *
5529 op_copy_shadowed_layer_txn(svn_wc__db_wcroot_t *wcroot,
5530 struct op_copy_baton *ocb,
5531 apr_pool_t *scratch_pool)
5533 const char *src_parent_relpath;
5534 const char *dst_parent_relpath;
5538 const char *repos_relpath = NULL;
5539 apr_int64_t repos_id = INVALID_REPOS_ID;
5540 svn_revnum_t revision = SVN_INVALID_REVNUM;
5542 if (wcroot != ocb->dst_wcroot)
5544 /* Source and destination databases differ; so also start a lock
5545 in the destination database, by calling ourself in an extra lock. */
5547 SVN_WC__DB_WITH_TXN(op_copy_shadowed_layer_txn(ocb->dst_wcroot, ocb,
5551 return SVN_NO_ERROR;
5554 /* From this point we can assume a lock in the src and dst databases */
5557 /* src_relpath and dst_relpath can't be wcroot as we need their parents */
5558 SVN_ERR_ASSERT(*ocb->src_relpath && *ocb->dst_relpath);
5560 src_parent_relpath = svn_relpath_dirname(ocb->src_relpath, scratch_pool);
5561 dst_parent_relpath = svn_relpath_dirname(ocb->dst_relpath, scratch_pool);
5563 /* src_parent must be status normal or added; get its op-depth */
5564 SVN_ERR(op_depth_of(&src_op_depth, ocb->src_wcroot, src_parent_relpath));
5566 /* dst_parent must be status added; get its op-depth */
5567 SVN_ERR(op_depth_of(&dst_op_depth, ocb->dst_wcroot, dst_parent_relpath));
5569 del_op_depth = relpath_depth(ocb->dst_relpath);
5571 /* Get some information from the parent */
5572 SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, &revision, &repos_relpath,
5573 &repos_id, NULL, NULL, NULL, NULL, NULL,
5576 src_parent_relpath, src_op_depth,
5577 scratch_pool, scratch_pool));
5579 if (repos_relpath == NULL)
5581 /* The node is a local addition and has no shadowed information */
5582 return SVN_NO_ERROR;
5585 /* And calculate the child repos relpath */
5586 repos_relpath = svn_relpath_join(repos_relpath,
5587 svn_relpath_basename(ocb->src_relpath,
5591 SVN_ERR(db_op_copy_shadowed_layer(
5592 ocb->src_wcroot, ocb->src_relpath, src_op_depth,
5593 ocb->dst_wcroot, ocb->dst_relpath, dst_op_depth,
5595 repos_id, repos_relpath, revision,
5596 (ocb->is_move ? dst_op_depth : 0),
5599 return SVN_NO_ERROR;
5603 svn_wc__db_op_copy_shadowed_layer(svn_wc__db_t *db,
5604 const char *src_abspath,
5605 const char *dst_abspath,
5606 svn_boolean_t is_move,
5607 apr_pool_t *scratch_pool)
5609 struct op_copy_baton ocb = {0};
5611 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
5612 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
5614 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
5615 &ocb.src_relpath, db,
5617 scratch_pool, scratch_pool));
5618 VERIFY_USABLE_WCROOT(ocb.src_wcroot);
5620 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
5623 scratch_pool, scratch_pool));
5624 VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
5626 ocb.is_move = is_move;
5627 ocb.dst_op_root_relpath = NULL; /* not used by op_copy_shadowed_layer_txn */
5629 ocb.work_items = NULL;
5631 /* Call with the sdb in src_wcroot. It might call itself again to
5632 also obtain a lock in dst_wcroot */
5633 SVN_WC__DB_WITH_TXN(op_copy_shadowed_layer_txn(ocb.src_wcroot, &ocb,
5637 return SVN_NO_ERROR;
5641 /* If there are any server-excluded base nodes then the copy must fail
5642 as it's not possible to commit such a copy.
5643 Return an error if there are any server-excluded nodes. */
5644 static svn_error_t *
5645 catch_copy_of_server_excluded(svn_wc__db_wcroot_t *wcroot,
5646 const char *local_relpath,
5647 apr_pool_t *scratch_pool)
5649 svn_sqlite__stmt_t *stmt;
5650 svn_boolean_t have_row;
5651 const char *server_excluded_relpath;
5653 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5654 STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
5655 SVN_ERR(svn_sqlite__bindf(stmt, "is",
5658 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5660 server_excluded_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
5661 SVN_ERR(svn_sqlite__reset(stmt));
5663 return svn_error_createf(SVN_ERR_AUTHZ_UNREADABLE, NULL,
5664 _("Cannot copy '%s' excluded by server"),
5665 path_for_error_message(wcroot,
5666 server_excluded_relpath,
5669 return SVN_NO_ERROR;
5674 svn_wc__db_op_copy_dir(svn_wc__db_t *db,
5675 const char *local_abspath,
5676 const apr_hash_t *props,
5677 svn_revnum_t changed_rev,
5678 apr_time_t changed_date,
5679 const char *changed_author,
5680 const char *original_repos_relpath,
5681 const char *original_root_url,
5682 const char *original_uuid,
5683 svn_revnum_t original_revision,
5684 const apr_array_header_t *children,
5686 svn_boolean_t is_move,
5687 const svn_skel_t *conflict,
5688 const svn_skel_t *work_items,
5689 apr_pool_t *scratch_pool)
5691 svn_wc__db_wcroot_t *wcroot;
5692 const char *local_relpath;
5693 insert_working_baton_t iwb;
5694 int parent_op_depth;
5696 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5697 SVN_ERR_ASSERT(props != NULL);
5698 /* ### any assertions for CHANGED_* ? */
5699 /* ### any assertions for ORIGINAL_* ? */
5701 SVN_ERR_ASSERT(children != NULL);
5704 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5705 local_abspath, scratch_pool, scratch_pool));
5706 VERIFY_USABLE_WCROOT(wcroot);
5710 iwb.presence = svn_wc__db_status_normal;
5711 iwb.kind = svn_node_dir;
5713 if (original_root_url != NULL)
5715 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5716 original_root_url, original_uuid,
5717 wcroot->sdb, scratch_pool));
5718 iwb.original_repos_relpath = original_repos_relpath;
5719 iwb.original_revnum = original_revision;
5722 iwb.changed_rev = changed_rev;
5723 iwb.changed_date = changed_date;
5724 iwb.changed_author = changed_author;
5727 /* ### Should we do this inside the transaction? */
5728 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5729 &parent_op_depth, iwb.original_repos_id,
5730 original_repos_relpath, original_revision,
5731 wcroot, local_relpath, scratch_pool));
5733 iwb.children = children;
5735 iwb.moved_here = is_move && (parent_op_depth == 0 ||
5736 iwb.op_depth == parent_op_depth);
5738 iwb.work_items = work_items;
5739 iwb.conflict = conflict;
5741 SVN_WC__DB_WITH_TXN(
5742 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5744 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
5746 return SVN_NO_ERROR;
5751 svn_wc__db_op_copy_file(svn_wc__db_t *db,
5752 const char *local_abspath,
5753 const apr_hash_t *props,
5754 svn_revnum_t changed_rev,
5755 apr_time_t changed_date,
5756 const char *changed_author,
5757 const char *original_repos_relpath,
5758 const char *original_root_url,
5759 const char *original_uuid,
5760 svn_revnum_t original_revision,
5761 const svn_checksum_t *checksum,
5762 svn_boolean_t update_actual_props,
5763 const apr_hash_t *new_actual_props,
5764 svn_boolean_t is_move,
5765 const svn_skel_t *conflict,
5766 const svn_skel_t *work_items,
5767 apr_pool_t *scratch_pool)
5769 svn_wc__db_wcroot_t *wcroot;
5770 const char *local_relpath;
5771 insert_working_baton_t iwb;
5772 int parent_op_depth;
5774 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5775 SVN_ERR_ASSERT(props != NULL);
5776 /* ### any assertions for CHANGED_* ? */
5777 SVN_ERR_ASSERT((! original_repos_relpath && ! original_root_url
5778 && ! original_uuid && ! checksum
5779 && original_revision == SVN_INVALID_REVNUM)
5780 || (original_repos_relpath && original_root_url
5781 && original_uuid && checksum
5782 && original_revision != SVN_INVALID_REVNUM));
5784 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5785 local_abspath, scratch_pool, scratch_pool));
5786 VERIFY_USABLE_WCROOT(wcroot);
5790 iwb.presence = svn_wc__db_status_normal;
5791 iwb.kind = svn_node_file;
5793 if (original_root_url != NULL)
5795 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5796 original_root_url, original_uuid,
5797 wcroot->sdb, scratch_pool));
5798 iwb.original_repos_relpath = original_repos_relpath;
5799 iwb.original_revnum = original_revision;
5802 iwb.changed_rev = changed_rev;
5803 iwb.changed_date = changed_date;
5804 iwb.changed_author = changed_author;
5807 /* ### Should we do this inside the transaction? */
5808 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5809 &parent_op_depth, iwb.original_repos_id,
5810 original_repos_relpath, original_revision,
5811 wcroot, local_relpath, scratch_pool));
5813 iwb.checksum = checksum;
5814 iwb.moved_here = is_move && (parent_op_depth == 0 ||
5815 iwb.op_depth == parent_op_depth);
5817 if (update_actual_props)
5819 iwb.update_actual_props = update_actual_props;
5820 iwb.new_actual_props = new_actual_props;
5823 iwb.work_items = work_items;
5824 iwb.conflict = conflict;
5826 SVN_WC__DB_WITH_TXN(
5827 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5829 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5831 return SVN_NO_ERROR;
5836 svn_wc__db_op_copy_symlink(svn_wc__db_t *db,
5837 const char *local_abspath,
5838 const apr_hash_t *props,
5839 svn_revnum_t changed_rev,
5840 apr_time_t changed_date,
5841 const char *changed_author,
5842 const char *original_repos_relpath,
5843 const char *original_root_url,
5844 const char *original_uuid,
5845 svn_revnum_t original_revision,
5847 svn_boolean_t is_move,
5848 const svn_skel_t *conflict,
5849 const svn_skel_t *work_items,
5850 apr_pool_t *scratch_pool)
5852 svn_wc__db_wcroot_t *wcroot;
5853 const char *local_relpath;
5854 insert_working_baton_t iwb;
5855 int parent_op_depth;
5857 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5858 SVN_ERR_ASSERT(props != NULL);
5859 /* ### any assertions for CHANGED_* ? */
5860 /* ### any assertions for ORIGINAL_* ? */
5861 SVN_ERR_ASSERT(target != NULL);
5863 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5864 local_abspath, scratch_pool, scratch_pool));
5865 VERIFY_USABLE_WCROOT(wcroot);
5869 iwb.presence = svn_wc__db_status_normal;
5870 iwb.kind = svn_node_symlink;
5873 if (original_root_url != NULL)
5875 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5876 original_root_url, original_uuid,
5877 wcroot->sdb, scratch_pool));
5878 iwb.original_repos_relpath = original_repos_relpath;
5879 iwb.original_revnum = original_revision;
5882 iwb.changed_rev = changed_rev;
5883 iwb.changed_date = changed_date;
5884 iwb.changed_author = changed_author;
5887 /* ### Should we do this inside the transaction? */
5888 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5889 &parent_op_depth, iwb.original_repos_id,
5890 original_repos_relpath, original_revision,
5891 wcroot, local_relpath, scratch_pool));
5893 iwb.target = target;
5894 iwb.moved_here = is_move && (parent_op_depth == 0 ||
5895 iwb.op_depth == parent_op_depth);
5897 iwb.work_items = work_items;
5898 iwb.conflict = conflict;
5900 SVN_WC__DB_WITH_TXN(
5901 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5903 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5905 return SVN_NO_ERROR;
5910 svn_wc__db_op_add_directory(svn_wc__db_t *db,
5911 const char *local_abspath,
5912 const apr_hash_t *props,
5913 const svn_skel_t *work_items,
5914 apr_pool_t *scratch_pool)
5916 svn_wc__db_wcroot_t *wcroot;
5917 const char *local_relpath;
5918 const char *dir_abspath;
5920 insert_working_baton_t iwb;
5922 /* Resolve wcroot via parent directory to avoid obstruction handling */
5923 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5924 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5926 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5927 dir_abspath, scratch_pool, scratch_pool));
5928 VERIFY_USABLE_WCROOT(wcroot);
5932 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5933 iwb.presence = svn_wc__db_status_normal;
5934 iwb.kind = svn_node_dir;
5935 iwb.op_depth = relpath_depth(local_relpath);
5936 if (props && apr_hash_count((apr_hash_t *)props))
5938 iwb.update_actual_props = TRUE;
5939 iwb.new_actual_props = props;
5942 iwb.work_items = work_items;
5944 SVN_WC__DB_WITH_TXN(
5945 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5947 /* Use depth infinity to make sure we have no invalid cached information
5948 * about children of this dir. */
5949 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5952 return SVN_NO_ERROR;
5957 svn_wc__db_op_add_file(svn_wc__db_t *db,
5958 const char *local_abspath,
5959 const apr_hash_t *props,
5960 const svn_skel_t *work_items,
5961 apr_pool_t *scratch_pool)
5963 svn_wc__db_wcroot_t *wcroot;
5964 const char *local_relpath;
5965 insert_working_baton_t iwb;
5966 const char *dir_abspath;
5969 /* Resolve wcroot via parent directory to avoid obstruction handling */
5970 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5971 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5973 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5974 dir_abspath, scratch_pool, scratch_pool));
5975 VERIFY_USABLE_WCROOT(wcroot);
5979 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5980 iwb.presence = svn_wc__db_status_normal;
5981 iwb.kind = svn_node_file;
5982 iwb.op_depth = relpath_depth(local_relpath);
5983 if (props && apr_hash_count((apr_hash_t *)props))
5985 iwb.update_actual_props = TRUE;
5986 iwb.new_actual_props = props;
5989 iwb.work_items = work_items;
5991 SVN_WC__DB_WITH_TXN(
5992 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5994 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5996 return SVN_NO_ERROR;
6001 svn_wc__db_op_add_symlink(svn_wc__db_t *db,
6002 const char *local_abspath,
6004 const apr_hash_t *props,
6005 const svn_skel_t *work_items,
6006 apr_pool_t *scratch_pool)
6008 svn_wc__db_wcroot_t *wcroot;
6009 const char *local_relpath;
6010 insert_working_baton_t iwb;
6011 const char *dir_abspath;
6014 /* Resolve wcroot via parent directory to avoid obstruction handling */
6015 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6016 SVN_ERR_ASSERT(target != NULL);
6018 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
6020 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6021 dir_abspath, scratch_pool, scratch_pool));
6023 VERIFY_USABLE_WCROOT(wcroot);
6027 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
6028 iwb.presence = svn_wc__db_status_normal;
6029 iwb.kind = svn_node_symlink;
6030 iwb.op_depth = relpath_depth(local_relpath);
6031 if (props && apr_hash_count((apr_hash_t *)props))
6033 iwb.update_actual_props = TRUE;
6034 iwb.new_actual_props = props;
6037 iwb.target = target;
6039 iwb.work_items = work_items;
6041 SVN_WC__DB_WITH_TXN(
6042 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
6044 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6046 return SVN_NO_ERROR;
6049 /* Record RECORDED_SIZE and RECORDED_TIME into top layer in NODES */
6050 static svn_error_t *
6051 db_record_fileinfo(svn_wc__db_wcroot_t *wcroot,
6052 const char *local_relpath,
6053 apr_int64_t recorded_size,
6054 apr_int64_t recorded_time,
6055 apr_pool_t *scratch_pool)
6057 svn_sqlite__stmt_t *stmt;
6060 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6061 STMT_UPDATE_NODE_FILEINFO));
6062 SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
6063 recorded_size, recorded_time));
6064 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6066 SVN_ERR_ASSERT(affected_rows == 1);
6068 return SVN_NO_ERROR;
6073 svn_wc__db_global_record_fileinfo(svn_wc__db_t *db,
6074 const char *local_abspath,
6075 svn_filesize_t recorded_size,
6076 apr_time_t recorded_time,
6077 apr_pool_t *scratch_pool)
6079 svn_wc__db_wcroot_t *wcroot;
6080 const char *local_relpath;
6082 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6084 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6085 local_abspath, scratch_pool, scratch_pool));
6086 VERIFY_USABLE_WCROOT(wcroot);
6088 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
6089 recorded_size, recorded_time, scratch_pool));
6091 /* We *totally* monkeyed the entries. Toss 'em. */
6092 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6094 return SVN_NO_ERROR;
6098 /* Set the ACTUAL_NODE properties column for (WC_ID, LOCAL_RELPATH) to
6101 * Note: PROPS=NULL means the actual props are the same as the pristine
6102 * props; to indicate no properties when the pristine has some props,
6103 * PROPS must be an empty hash. */
6104 static svn_error_t *
6105 set_actual_props(svn_wc__db_wcroot_t *wcroot,
6106 const char *local_relpath,
6108 apr_pool_t *scratch_pool)
6110 svn_sqlite__stmt_t *stmt;
6113 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6114 STMT_UPDATE_ACTUAL_PROPS));
6115 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6116 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
6117 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6119 if (affected_rows == 1 || !props)
6121 /* Perhaps the entire ACTUAL record is unneeded now? */
6122 if (!props && affected_rows)
6124 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6125 STMT_DELETE_ACTUAL_EMPTY));
6126 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6127 SVN_ERR(svn_sqlite__step_done(stmt));
6130 return SVN_NO_ERROR; /* We are done */
6133 /* We have to insert a row in ACTUAL */
6135 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6136 STMT_INSERT_ACTUAL_PROPS));
6137 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6138 if (*local_relpath != '\0')
6139 SVN_ERR(svn_sqlite__bind_text(stmt, 3,
6140 svn_relpath_dirname(local_relpath,
6142 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
6143 return svn_error_trace(svn_sqlite__step_done(stmt));
6147 svn_wc__db_op_set_props_internal(svn_wc__db_wcroot_t *wcroot,
6148 const char *local_relpath,
6150 svn_boolean_t clear_recorded_info,
6151 apr_pool_t *scratch_pool)
6153 SVN_ERR(set_actual_props(wcroot, local_relpath, props, scratch_pool));
6155 if (clear_recorded_info)
6157 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
6158 SVN_INVALID_FILESIZE, 0,
6162 return SVN_NO_ERROR;
6165 /* The body of svn_wc__db_op_set_props().
6167 Set the 'properties' column in the 'ACTUAL_NODE' table to BATON->props.
6168 Create an entry in the ACTUAL table for the node if it does not yet
6170 To specify no properties, BATON->props must be an empty hash, not NULL.
6171 BATON is of type 'struct set_props_baton_t'.
6173 static svn_error_t *
6174 set_props_txn(svn_wc__db_wcroot_t *wcroot,
6175 const char *local_relpath,
6177 svn_boolean_t clear_recorded_info,
6178 const svn_skel_t *conflict,
6179 const svn_skel_t *work_items,
6180 apr_pool_t *scratch_pool)
6182 apr_hash_t *pristine_props;
6184 /* Check if the props are modified. If no changes, then wipe out the
6185 ACTUAL props. PRISTINE_PROPS==NULL means that any
6186 ACTUAL props are okay as provided, so go ahead and set them. */
6187 SVN_ERR(db_read_pristine_props(&pristine_props, wcroot, local_relpath, FALSE,
6188 scratch_pool, scratch_pool));
6189 if (props && pristine_props)
6191 apr_array_header_t *prop_diffs;
6193 SVN_ERR(svn_prop_diffs(&prop_diffs, props, pristine_props,
6195 if (prop_diffs->nelts == 0)
6199 SVN_ERR(svn_wc__db_op_set_props_internal(wcroot, local_relpath, props,
6200 clear_recorded_info, scratch_pool));
6203 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6205 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6206 conflict, scratch_pool));
6208 return SVN_NO_ERROR;
6213 svn_wc__db_op_set_props(svn_wc__db_t *db,
6214 const char *local_abspath,
6216 svn_boolean_t clear_recorded_info,
6217 const svn_skel_t *conflict,
6218 const svn_skel_t *work_items,
6219 apr_pool_t *scratch_pool)
6221 svn_wc__db_wcroot_t *wcroot;
6222 const char *local_relpath;
6224 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6226 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6227 db, local_abspath, scratch_pool, scratch_pool));
6228 VERIFY_USABLE_WCROOT(wcroot);
6230 SVN_WC__DB_WITH_TXN(set_props_txn(wcroot, local_relpath, props,
6231 clear_recorded_info, conflict, work_items,
6234 return SVN_NO_ERROR;
6239 svn_wc__db_op_modified(svn_wc__db_t *db,
6240 const char *local_abspath,
6241 apr_pool_t *scratch_pool)
6243 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6249 static svn_error_t *
6250 populate_targets_tree(svn_wc__db_wcroot_t *wcroot,
6251 const char *local_relpath,
6253 const apr_array_header_t *changelist_filter,
6254 apr_pool_t *scratch_pool)
6256 svn_sqlite__stmt_t *stmt;
6257 int affected_rows = 0;
6258 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6259 STMT_CREATE_TARGETS_LIST));
6261 if (changelist_filter && changelist_filter->nelts > 0)
6263 /* Iterate over the changelists, adding the nodes which match.
6264 Common case: we only have one changelist, so this only
6271 case svn_depth_empty:
6272 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST;
6275 case svn_depth_files:
6276 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES;
6279 case svn_depth_immediates:
6280 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES;
6283 case svn_depth_infinity:
6284 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY;
6288 /* We don't know how to handle unknown or exclude. */
6289 SVN_ERR_MALFUNCTION();
6293 for (i = 0; i < changelist_filter->nelts; i++)
6296 const char *changelist = APR_ARRAY_IDX(changelist_filter, i,
6299 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6300 STMT_INSERT_TARGET_WITH_CHANGELIST));
6301 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
6302 local_relpath, changelist));
6303 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6305 /* If the root is matched by the changelist, we don't have to match
6306 the children. As that tells us the root is a file */
6307 if (!sub_affected && depth > svn_depth_empty)
6309 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
6310 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
6311 local_relpath, changelist));
6312 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6315 affected_rows += sub_affected;
6318 else /* No changelist filtering */
6325 case svn_depth_empty:
6326 stmt_idx = STMT_INSERT_TARGET;
6329 case svn_depth_files:
6330 stmt_idx = STMT_INSERT_TARGET_DEPTH_FILES;
6333 case svn_depth_immediates:
6334 stmt_idx = STMT_INSERT_TARGET_DEPTH_IMMEDIATES;
6337 case svn_depth_infinity:
6338 stmt_idx = STMT_INSERT_TARGET_DEPTH_INFINITY;
6342 /* We don't know how to handle unknown or exclude. */
6343 SVN_ERR_MALFUNCTION();
6347 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6348 STMT_INSERT_TARGET));
6349 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6350 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6351 affected_rows += sub_affected;
6353 if (depth > svn_depth_empty)
6355 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
6356 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6357 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6358 affected_rows += sub_affected;
6362 /* Does the target exist? */
6363 if (affected_rows == 0)
6365 svn_boolean_t exists;
6366 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
6369 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6370 _("The node '%s' was not found."),
6371 path_for_error_message(wcroot,
6376 return SVN_NO_ERROR;
6381 static svn_error_t *
6382 dump_targets(svn_wc__db_wcroot_t *wcroot,
6383 apr_pool_t *scratch_pool)
6385 svn_sqlite__stmt_t *stmt;
6386 svn_boolean_t have_row;
6388 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6389 STMT_SELECT_TARGETS));
6390 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6393 const char *target = svn_sqlite__column_text(stmt, 0, NULL);
6394 SVN_DBG(("Target: '%s'\n", target));
6395 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6398 SVN_ERR(svn_sqlite__reset(stmt));
6400 return SVN_NO_ERROR;
6405 struct set_changelist_baton_t
6407 const char *new_changelist;
6408 const apr_array_header_t *changelist_filter;
6413 /* The main part of svn_wc__db_op_set_changelist().
6415 * Implements svn_wc__db_txn_callback_t. */
6416 static svn_error_t *
6417 set_changelist_txn(void *baton,
6418 svn_wc__db_wcroot_t *wcroot,
6419 const char *local_relpath,
6420 apr_pool_t *scratch_pool)
6422 struct set_changelist_baton_t *scb = baton;
6423 svn_sqlite__stmt_t *stmt;
6425 SVN_ERR(populate_targets_tree(wcroot, local_relpath, scb->depth,
6426 scb->changelist_filter, scratch_pool));
6428 /* Ensure we have actual nodes for our targets. */
6429 if (scb->new_changelist)
6431 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6432 STMT_INSERT_ACTUAL_EMPTIES_FILES));
6433 SVN_ERR(svn_sqlite__step_done(stmt));
6436 /* Now create our notification table. */
6437 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6438 STMT_CREATE_CHANGELIST_LIST));
6439 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6440 STMT_CREATE_CHANGELIST_TRIGGER));
6442 /* Update our changelists. */
6443 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6444 STMT_UPDATE_ACTUAL_CHANGELISTS));
6445 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6446 scb->new_changelist));
6447 SVN_ERR(svn_sqlite__step_done(stmt));
6449 if (scb->new_changelist)
6451 /* We have to notify that we skipped directories, so do that now. */
6452 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6453 STMT_MARK_SKIPPED_CHANGELIST_DIRS));
6454 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6455 scb->new_changelist));
6456 SVN_ERR(svn_sqlite__step_done(stmt));
6459 /* We may have left empty ACTUAL nodes, so remove them. This is only a
6460 potential problem if we removed changelists. */
6461 if (!scb->new_changelist)
6463 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6464 STMT_DELETE_ACTUAL_EMPTIES));
6465 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6466 SVN_ERR(svn_sqlite__step_done(stmt));
6469 return SVN_NO_ERROR;
6473 /* Send notifications for svn_wc__db_op_set_changelist().
6475 * Implements work_callback_t. */
6476 static svn_error_t *
6477 do_changelist_notify(void *baton,
6478 svn_wc__db_wcroot_t *wcroot,
6479 svn_cancel_func_t cancel_func,
6481 svn_wc_notify_func2_t notify_func,
6483 apr_pool_t *scratch_pool)
6485 svn_sqlite__stmt_t *stmt;
6486 svn_boolean_t have_row;
6487 apr_pool_t *iterpool;
6489 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6490 STMT_SELECT_CHANGELIST_LIST));
6491 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6493 iterpool = svn_pool_create(scratch_pool);
6496 /* ### wc_id is column 0. use it one day... */
6497 const char *notify_relpath = svn_sqlite__column_text(stmt, 1, NULL);
6498 svn_wc_notify_action_t action = svn_sqlite__column_int(stmt, 2);
6499 svn_wc_notify_t *notify;
6500 const char *notify_abspath;
6502 svn_pool_clear(iterpool);
6506 svn_error_t *err = cancel_func(cancel_baton);
6509 return svn_error_trace(svn_error_compose_create(
6511 svn_sqlite__reset(stmt)));
6514 notify_abspath = svn_dirent_join(wcroot->abspath, notify_relpath,
6516 notify = svn_wc_create_notify(notify_abspath, action, iterpool);
6517 notify->changelist_name = svn_sqlite__column_text(stmt, 3, NULL);
6518 notify_func(notify_baton, notify, iterpool);
6520 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6522 svn_pool_destroy(iterpool);
6524 return svn_error_trace(svn_sqlite__reset(stmt));
6529 svn_wc__db_op_set_changelist(svn_wc__db_t *db,
6530 const char *local_abspath,
6531 const char *new_changelist,
6532 const apr_array_header_t *changelist_filter,
6534 svn_wc_notify_func2_t notify_func,
6536 svn_cancel_func_t cancel_func,
6538 apr_pool_t *scratch_pool)
6540 svn_wc__db_wcroot_t *wcroot;
6541 const char *local_relpath;
6542 struct set_changelist_baton_t scb;
6544 scb.new_changelist = new_changelist;
6545 scb.changelist_filter = changelist_filter;
6548 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6550 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6552 scratch_pool, scratch_pool));
6553 VERIFY_USABLE_WCROOT(wcroot);
6555 /* Flush the entries before we do the work. Even if no work is performed,
6556 the flush isn't a problem. */
6557 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6559 /* Perform the set-changelist operation (transactionally), perform any
6560 notifications necessary, and then clean out our temporary tables. */
6561 return svn_error_trace(with_finalization(wcroot, local_relpath,
6562 set_changelist_txn, &scb,
6563 do_changelist_notify, NULL,
6564 cancel_func, cancel_baton,
6565 notify_func, notify_baton,
6566 STMT_FINALIZE_CHANGELIST,
6570 /* Implementation of svn_wc__db_op_mark_conflict() */
6572 svn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t *wcroot,
6573 const char *local_relpath,
6574 const svn_skel_t *conflict_skel,
6575 apr_pool_t *scratch_pool)
6577 svn_sqlite__stmt_t *stmt;
6578 svn_boolean_t got_row;
6579 svn_boolean_t is_complete;
6581 SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict_skel));
6582 SVN_ERR_ASSERT(is_complete);
6584 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6585 STMT_SELECT_ACTUAL_NODE));
6586 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6587 SVN_ERR(svn_sqlite__step(&got_row, stmt));
6588 SVN_ERR(svn_sqlite__reset(stmt));
6592 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6593 STMT_UPDATE_ACTUAL_CONFLICT));
6594 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6598 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6599 STMT_INSERT_ACTUAL_CONFLICT));
6600 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6601 if (*local_relpath != '\0')
6602 SVN_ERR(svn_sqlite__bind_text(stmt, 4,
6603 svn_relpath_dirname(local_relpath,
6608 svn_stringbuf_t *sb = svn_skel__unparse(conflict_skel, scratch_pool);
6610 SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6613 SVN_ERR(svn_sqlite__update(NULL, stmt));
6615 return SVN_NO_ERROR;
6619 svn_wc__db_op_mark_conflict(svn_wc__db_t *db,
6620 const char *local_abspath,
6621 const svn_skel_t *conflict_skel,
6622 const svn_skel_t *work_items,
6623 apr_pool_t *scratch_pool)
6625 svn_wc__db_wcroot_t *wcroot;
6626 const char *local_relpath;
6628 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6630 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6631 local_abspath, scratch_pool, scratch_pool));
6632 VERIFY_USABLE_WCROOT(wcroot);
6634 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6635 conflict_skel, scratch_pool));
6637 /* ### Should be handled in the same transaction as setting the conflict */
6639 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6641 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6643 return SVN_NO_ERROR;
6647 /* The body of svn_wc__db_op_mark_resolved().
6650 svn_wc__db_op_mark_resolved_internal(svn_wc__db_wcroot_t *wcroot,
6651 const char *local_relpath,
6653 svn_boolean_t resolved_text,
6654 svn_boolean_t resolved_props,
6655 svn_boolean_t resolved_tree,
6656 const svn_skel_t *work_items,
6657 apr_pool_t *scratch_pool)
6659 svn_sqlite__stmt_t *stmt;
6660 svn_boolean_t have_row;
6661 int total_affected_rows = 0;
6662 svn_boolean_t resolved_all;
6663 apr_size_t conflict_len;
6664 const void *conflict_data;
6665 svn_skel_t *conflicts;
6667 /* Check if we have a conflict in ACTUAL */
6668 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6669 STMT_SELECT_ACTUAL_NODE));
6670 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6672 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6676 SVN_ERR(svn_sqlite__reset(stmt));
6678 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6679 STMT_SELECT_NODE_INFO));
6681 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6683 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6684 SVN_ERR(svn_sqlite__reset(stmt));
6687 return SVN_NO_ERROR;
6689 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6690 _("The node '%s' was not found."),
6691 path_for_error_message(wcroot,
6696 conflict_data = svn_sqlite__column_blob(stmt, 2, &conflict_len,
6698 SVN_ERR(svn_sqlite__reset(stmt));
6699 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6702 return SVN_NO_ERROR;
6704 conflicts = svn_skel__parse(conflict_data, conflict_len, scratch_pool);
6707 SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
6708 db, wcroot->abspath,
6710 resolved_props ? "" : NULL,
6712 scratch_pool, scratch_pool));
6714 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6715 STMT_UPDATE_ACTUAL_CONFLICT));
6716 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6720 svn_stringbuf_t *sb = svn_skel__unparse(conflicts, scratch_pool);
6722 SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6725 SVN_ERR(svn_sqlite__update(&total_affected_rows, stmt));
6727 /* Now, remove the actual node if it doesn't have any more useful
6728 information. We only need to do this if we've remove data ourselves. */
6729 if (total_affected_rows > 0)
6731 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6732 STMT_DELETE_ACTUAL_EMPTY));
6733 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6734 SVN_ERR(svn_sqlite__step_done(stmt));
6737 return SVN_NO_ERROR;
6741 svn_wc__db_op_mark_resolved(svn_wc__db_t *db,
6742 const char *local_abspath,
6743 svn_boolean_t resolved_text,
6744 svn_boolean_t resolved_props,
6745 svn_boolean_t resolved_tree,
6746 const svn_skel_t *work_items,
6747 apr_pool_t *scratch_pool)
6749 svn_wc__db_wcroot_t *wcroot;
6750 const char *local_relpath;
6752 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6754 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6755 local_abspath, scratch_pool, scratch_pool));
6756 VERIFY_USABLE_WCROOT(wcroot);
6758 SVN_WC__DB_WITH_TXN(
6759 svn_wc__db_op_mark_resolved_internal(
6760 wcroot, local_relpath, db,
6761 resolved_text, resolved_props, resolved_tree,
6762 work_items, scratch_pool),
6765 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6766 return SVN_NO_ERROR;
6769 /* Clear moved-to information at the delete-half of the move which moved
6770 * MOVED_TO_RELPATH here. This transforms the delete part of the move into a
6773 * Note that the moved-to location is always an op-root, while this is not the
6774 * case for a moved-from location.
6776 static svn_error_t *
6777 clear_moved_to(svn_wc__db_wcroot_t *wcroot,
6778 const char *moved_to_relpath,
6779 apr_pool_t *scratch_pool)
6781 svn_sqlite__stmt_t *stmt;
6782 const char *moved_from_relpath;
6783 int moved_from_op_depth;
6785 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6786 STMT_SELECT_MOVED_FROM_RELPATH));
6787 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, moved_to_relpath));
6788 SVN_ERR(svn_sqlite__step_row(stmt));
6790 moved_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
6791 moved_from_op_depth = svn_sqlite__column_int(stmt, 1);
6792 SVN_ERR(svn_sqlite__reset(stmt));
6794 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6795 STMT_CLEAR_MOVED_TO_RELPATH));
6796 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6797 moved_from_relpath, moved_from_op_depth));
6798 SVN_ERR(svn_sqlite__update(NULL, stmt));
6800 return SVN_NO_ERROR;
6803 /* Helper function for op_revert_txn. Raises move tree conflicts on
6804 descendants to ensure database stability on a non recursive revert
6805 of an ancestor that contains a possible move related tree conflict.
6807 static svn_error_t *
6808 revert_maybe_raise_moved_away(svn_wc__db_wcroot_t * wcroot,
6810 const char *local_relpath,
6812 apr_pool_t *scratch_pool)
6814 svn_skel_t *conflict;
6815 svn_wc_operation_t operation;
6816 svn_boolean_t tree_conflicted;
6817 const apr_array_header_t *locations;
6818 svn_wc_conflict_reason_t reason;
6819 svn_wc_conflict_action_t action;
6821 SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, wcroot,
6823 scratch_pool, scratch_pool));
6826 return SVN_NO_ERROR;
6828 SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, NULL, NULL,
6830 db, wcroot->abspath,
6832 scratch_pool, scratch_pool));
6834 if (!tree_conflicted
6835 || (operation != svn_wc_operation_update
6836 && operation != svn_wc_operation_switch))
6838 return SVN_NO_ERROR;
6841 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
6843 db, wcroot->abspath,
6848 if (reason == svn_wc_conflict_reason_deleted
6849 || reason == svn_wc_conflict_reason_replaced)
6851 SVN_ERR(svn_wc__db_op_raise_moved_away_internal(
6852 wcroot, local_relpath, op_depth_below, db,
6854 (locations && locations->nelts > 0)
6855 ? APR_ARRAY_IDX(locations, 0,
6856 const svn_wc_conflict_version_t *)
6858 (locations && locations->nelts > 1)
6859 ? APR_ARRAY_IDX(locations, 1,
6860 const svn_wc_conflict_version_t *)
6864 /* Transform the move information into revert information */
6865 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6866 STMT_MOVE_NOTIFY_TO_REVERT));
6869 return SVN_NO_ERROR;
6872 /* Baton for op_revert_txn and op_revert_recursive_txn */
6873 struct revert_baton_t
6876 svn_boolean_t clear_changelists;
6879 /* One of the two alternative bodies of svn_wc__db_op_revert().
6881 * Implements svn_wc__db_txn_callback_t. */
6882 static svn_error_t *
6883 op_revert_txn(void *baton,
6884 svn_wc__db_wcroot_t *wcroot,
6885 const char *local_relpath,
6886 apr_pool_t *scratch_pool)
6888 struct revert_baton_t *rvb = baton;
6889 svn_wc__db_t *db = rvb->db;
6890 svn_sqlite__stmt_t *stmt;
6891 svn_boolean_t have_row;
6893 svn_boolean_t moved_here;
6895 const char *moved_to;
6898 /* ### Similar structure to op_revert_recursive_txn, should they be
6901 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6902 STMT_SELECT_NODE_INFO));
6903 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6904 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6907 SVN_ERR(svn_sqlite__reset(stmt));
6909 /* There was no NODE row, so attempt to delete an ACTUAL_NODE row. */
6910 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6911 STMT_DELETE_ACTUAL_NODE));
6912 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6913 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6916 /* Can't do non-recursive actual-only revert if actual-only
6917 children exist. Raise an error to cancel the transaction. */
6918 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6919 STMT_ACTUAL_HAS_CHILDREN));
6920 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6921 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6922 SVN_ERR(svn_sqlite__reset(stmt));
6924 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6925 _("Can't revert '%s' without"
6926 " reverting children"),
6927 path_for_error_message(wcroot,
6930 return SVN_NO_ERROR;
6933 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6934 _("The node '%s' was not found."),
6935 path_for_error_message(wcroot,
6940 op_depth = svn_sqlite__column_int(stmt, 0);
6941 moved_here = svn_sqlite__column_boolean(stmt, 15);
6942 moved_to = svn_sqlite__column_text(stmt, 17, scratch_pool);
6944 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6946 op_depth_below = svn_sqlite__column_int(stmt, 0);
6948 op_depth_below = -1;
6950 SVN_ERR(svn_sqlite__reset(stmt));
6954 SVN_ERR(svn_wc__db_op_break_move_internal(wcroot,
6955 local_relpath, op_depth,
6956 moved_to, NULL, scratch_pool));
6959 if (op_depth > 0 && op_depth == relpath_depth(local_relpath))
6961 int op_depth_increased;
6963 /* Can't do non-recursive revert if children exist */
6964 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6965 STMT_SELECT_GE_OP_DEPTH_CHILDREN));
6966 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6967 local_relpath, op_depth));
6968 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6969 SVN_ERR(svn_sqlite__reset(stmt));
6971 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6972 _("Can't revert '%s' without"
6973 " reverting children"),
6974 path_for_error_message(wcroot,
6978 /* Rewrite the op-depth of all deleted children making the
6979 direct children into roots of deletes. */
6980 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6981 STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE));
6982 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6985 SVN_ERR(svn_sqlite__update(&op_depth_increased, stmt));
6987 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6988 STMT_DELETE_WORKING_NODE));
6989 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6990 SVN_ERR(svn_sqlite__step_done(stmt));
6992 /* ### This removes the lock, but what about the access baton? */
6993 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6994 STMT_DELETE_WC_LOCK_ORPHAN));
6995 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6996 SVN_ERR(svn_sqlite__step_done(stmt));
6998 /* If this node was moved-here, clear moved-to at the move source. */
7000 SVN_ERR(clear_moved_to(wcroot, local_relpath, scratch_pool));
7002 /* If the node was moved itself, we don't have interesting moved
7003 children (and the move itself was already broken) */
7004 if (op_depth_increased && !moved_to)
7005 SVN_ERR(revert_maybe_raise_moved_away(wcroot, db, local_relpath,
7006 op_depth_below, scratch_pool));
7009 if (rvb->clear_changelists)
7011 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7012 STMT_DELETE_ACTUAL_NODE));
7013 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7014 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7018 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7019 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST));
7020 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7021 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7024 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7025 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST));
7026 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7027 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7031 return SVN_NO_ERROR;
7035 /* One of the two alternative bodies of svn_wc__db_op_revert().
7037 * Implements svn_wc__db_txn_callback_t. */
7038 static svn_error_t *
7039 op_revert_recursive_txn(void *baton,
7040 svn_wc__db_wcroot_t *wcroot,
7041 const char *local_relpath,
7042 apr_pool_t *scratch_pool)
7044 struct revert_baton_t *rvb = baton;
7045 svn_sqlite__stmt_t *stmt;
7046 svn_boolean_t have_row;
7048 int select_op_depth;
7049 svn_boolean_t moved_here;
7051 apr_pool_t *iterpool;
7053 /* ### Similar structure to op_revert_txn, should they be
7056 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7057 STMT_SELECT_NODE_INFO));
7058 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7059 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7062 SVN_ERR(svn_sqlite__reset(stmt));
7064 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7065 STMT_DELETE_ACTUAL_NODE));
7066 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7068 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7071 return SVN_NO_ERROR; /* actual-only revert */
7073 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7074 _("The node '%s' was not found."),
7075 path_for_error_message(wcroot,
7080 op_depth = svn_sqlite__column_int(stmt, 0);
7081 moved_here = svn_sqlite__column_boolean(stmt, 15);
7082 SVN_ERR(svn_sqlite__reset(stmt));
7084 if (op_depth > 0 && op_depth != relpath_depth(local_relpath))
7085 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
7086 _("Can't revert '%s' without"
7087 " reverting parent"),
7088 path_for_error_message(wcroot,
7092 /* Remove moved-here from move destinations outside the tree. */
7093 SVN_ERR(svn_sqlite__get_statement(
7094 &stmt, wcroot->sdb, STMT_SELECT_MOVED_OUTSIDE));
7095 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
7097 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7100 const char *src_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7101 const char *dst_relpath = svn_sqlite__column_text(stmt, 1, NULL);
7102 int move_op_depth = svn_sqlite__column_int(stmt, 2);
7105 err = svn_wc__db_op_break_move_internal(wcroot,
7106 src_relpath, move_op_depth,
7107 dst_relpath, NULL, scratch_pool);
7109 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
7111 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7113 SVN_ERR(svn_sqlite__reset(stmt));
7115 /* Don't delete BASE nodes */
7116 select_op_depth = op_depth ? op_depth : 1;
7118 /* Reverting any non wc-root node */
7119 SVN_ERR(svn_sqlite__get_statement(
7121 STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
7122 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
7123 local_relpath, select_op_depth));
7124 SVN_ERR(svn_sqlite__step_done(stmt));
7126 if (rvb->clear_changelists)
7128 SVN_ERR(svn_sqlite__get_statement(
7130 STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7131 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7132 SVN_ERR(svn_sqlite__step_done(stmt));
7136 SVN_ERR(svn_sqlite__get_statement(
7138 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
7139 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7140 SVN_ERR(svn_sqlite__step_done(stmt));
7142 SVN_ERR(svn_sqlite__get_statement(
7144 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
7145 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7146 SVN_ERR(svn_sqlite__step_done(stmt));
7149 /* ### This removes the locks, but what about the access batons? */
7150 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7151 STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
7152 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7154 SVN_ERR(svn_sqlite__step_done(stmt));
7156 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7157 STMT_SELECT_MOVED_HERE_CHILDREN));
7158 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7160 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7162 iterpool = svn_pool_create(scratch_pool);
7165 const char *moved_here_child_relpath;
7168 svn_pool_clear(iterpool);
7170 moved_here_child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
7171 err = clear_moved_to(wcroot, moved_here_child_relpath, iterpool);
7173 return svn_error_trace(svn_error_compose_create(
7175 svn_sqlite__reset(stmt)));
7177 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7179 SVN_ERR(svn_sqlite__reset(stmt));
7180 svn_pool_destroy(iterpool);
7182 /* Clear potential moved-to pointing at the target node itself. */
7183 if (op_depth > 0 && op_depth == relpath_depth(local_relpath)
7185 SVN_ERR(clear_moved_to(wcroot, local_relpath, scratch_pool));
7187 return SVN_NO_ERROR;
7191 svn_wc__db_op_revert(svn_wc__db_t *db,
7192 const char *local_abspath,
7194 svn_boolean_t clear_changelists,
7195 apr_pool_t *result_pool,
7196 apr_pool_t *scratch_pool)
7198 svn_wc__db_wcroot_t *wcroot;
7199 const char *local_relpath;
7200 struct revert_baton_t rvb;
7201 struct with_triggers_baton_t wtb = { STMT_CREATE_REVERT_LIST,
7202 STMT_DROP_REVERT_LIST_TRIGGERS,
7205 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7208 rvb.clear_changelists = clear_changelists;
7209 wtb.cb_baton = &rvb;
7213 case svn_depth_empty:
7214 wtb.cb_func = op_revert_txn;
7216 case svn_depth_infinity:
7217 wtb.cb_func = op_revert_recursive_txn;
7220 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
7221 _("Unsupported depth for revert of '%s'"),
7222 svn_dirent_local_style(local_abspath,
7226 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7227 db, local_abspath, scratch_pool, scratch_pool));
7228 VERIFY_USABLE_WCROOT(wcroot);
7230 SVN_WC__DB_WITH_TXN(with_triggers(&wtb, wcroot, local_relpath, scratch_pool),
7233 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
7235 return SVN_NO_ERROR;
7238 /* The body of svn_wc__db_revert_list_read().
7240 static svn_error_t *
7241 revert_list_read(svn_boolean_t *reverted,
7242 const apr_array_header_t **marker_paths,
7243 svn_boolean_t *copied_here,
7244 svn_node_kind_t *kind,
7245 svn_wc__db_wcroot_t *wcroot,
7246 const char *local_relpath,
7248 apr_pool_t *result_pool,
7249 apr_pool_t *scratch_pool)
7251 svn_sqlite__stmt_t *stmt;
7252 svn_boolean_t have_row;
7255 *marker_paths = NULL;
7256 *copied_here = FALSE;
7257 *kind = svn_node_unknown;
7259 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7260 STMT_SELECT_REVERT_LIST));
7261 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7262 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7265 svn_boolean_t is_actual = svn_sqlite__column_boolean(stmt, 0);
7266 svn_boolean_t another_row = FALSE;
7270 apr_size_t conflict_len;
7271 const void *conflict_data;
7273 conflict_data = svn_sqlite__column_blob(stmt, 5, &conflict_len,
7277 svn_skel_t *conflicts = svn_skel__parse(conflict_data,
7281 SVN_ERR(svn_wc__conflict_read_markers(marker_paths,
7282 db, wcroot->abspath,
7288 if (!svn_sqlite__column_is_null(stmt, 1)) /* notify */
7291 SVN_ERR(svn_sqlite__step(&another_row, stmt));
7294 if (!is_actual || another_row)
7297 if (!svn_sqlite__column_is_null(stmt, 4)) /* repos_id */
7299 int op_depth = svn_sqlite__column_int(stmt, 3);
7300 *copied_here = (op_depth == relpath_depth(local_relpath));
7302 *kind = svn_sqlite__column_token(stmt, 2, kind_map);
7306 SVN_ERR(svn_sqlite__reset(stmt));
7310 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7311 STMT_DELETE_REVERT_LIST));
7312 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7313 SVN_ERR(svn_sqlite__step_done(stmt));
7316 return SVN_NO_ERROR;
7320 svn_wc__db_revert_list_read(svn_boolean_t *reverted,
7321 const apr_array_header_t **marker_files,
7322 svn_boolean_t *copied_here,
7323 svn_node_kind_t *kind,
7325 const char *local_abspath,
7326 apr_pool_t *result_pool,
7327 apr_pool_t *scratch_pool)
7329 svn_wc__db_wcroot_t *wcroot;
7330 const char *local_relpath;
7332 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7333 db, local_abspath, scratch_pool, scratch_pool));
7334 VERIFY_USABLE_WCROOT(wcroot);
7336 SVN_WC__DB_WITH_TXN(
7337 revert_list_read(reverted, marker_files, copied_here, kind,
7338 wcroot, local_relpath, db,
7339 result_pool, scratch_pool),
7341 return SVN_NO_ERROR;
7345 /* The body of svn_wc__db_revert_list_read_copied_children().
7347 static svn_error_t *
7348 revert_list_read_copied_children(svn_wc__db_wcroot_t *wcroot,
7349 const char *local_relpath,
7350 apr_array_header_t **children_p,
7351 apr_pool_t *result_pool,
7352 apr_pool_t *scratch_pool)
7354 svn_sqlite__stmt_t *stmt;
7355 svn_boolean_t have_row;
7356 apr_array_header_t *children;
7359 apr_array_make(result_pool, 0,
7360 sizeof(svn_wc__db_revert_list_copied_child_info_t *));
7362 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7363 STMT_SELECT_REVERT_LIST_COPIED_CHILDREN));
7364 SVN_ERR(svn_sqlite__bindf(stmt, "sd",
7365 local_relpath, relpath_depth(local_relpath)));
7366 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7369 svn_wc__db_revert_list_copied_child_info_t *child_info;
7370 const char *child_relpath;
7372 child_info = apr_palloc(result_pool, sizeof(*child_info));
7374 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7375 child_info->abspath = svn_dirent_join(wcroot->abspath, child_relpath,
7377 child_info->kind = svn_sqlite__column_token(stmt, 1, kind_map);
7380 svn_wc__db_revert_list_copied_child_info_t *) = child_info;
7382 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7384 SVN_ERR(svn_sqlite__reset(stmt));
7386 *children_p = children;
7388 return SVN_NO_ERROR;
7393 svn_wc__db_revert_list_read_copied_children(apr_array_header_t **children,
7395 const char *local_abspath,
7396 apr_pool_t *result_pool,
7397 apr_pool_t *scratch_pool)
7399 svn_wc__db_wcroot_t *wcroot;
7400 const char *local_relpath;
7402 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7403 db, local_abspath, scratch_pool, scratch_pool));
7404 VERIFY_USABLE_WCROOT(wcroot);
7406 SVN_WC__DB_WITH_TXN(
7407 revert_list_read_copied_children(wcroot, local_relpath, children,
7408 result_pool, scratch_pool),
7410 return SVN_NO_ERROR;
7415 svn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func,
7418 const char *local_abspath,
7419 apr_pool_t *scratch_pool)
7421 svn_wc__db_wcroot_t *wcroot;
7422 const char *local_relpath;
7423 svn_sqlite__stmt_t *stmt;
7424 svn_boolean_t have_row;
7425 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
7427 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7428 db, local_abspath, scratch_pool, iterpool));
7429 VERIFY_USABLE_WCROOT(wcroot);
7431 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7432 STMT_SELECT_REVERT_LIST_RECURSIVE));
7433 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7434 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7436 return svn_error_trace(svn_sqlite__reset(stmt)); /* optimise for no row */
7439 const char *notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7440 svn_wc_notify_t *notify;
7442 svn_pool_clear(iterpool);
7444 notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
7447 svn_wc_notify_revert,
7450 if (!svn_sqlite__column_is_null(stmt, 1))
7451 notify->kind = svn_sqlite__column_token(stmt, 1, kind_map);
7454 if (!svn_sqlite__column_is_null(stmt, 3))
7455 notify->kind = svn_sqlite__column_token(stmt, 3, kind_map_none);
7457 switch (svn_sqlite__column_int(stmt, 2))
7462 /* standard revert */
7465 notify->action = svn_wc_notify_tree_conflict;
7468 SVN_ERR_MALFUNCTION();
7472 notify_func(notify_baton, notify, iterpool);
7474 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7476 SVN_ERR(svn_sqlite__reset(stmt));
7478 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7479 STMT_DELETE_REVERT_LIST_RECURSIVE));
7480 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7481 SVN_ERR(svn_sqlite__step_done(stmt));
7483 svn_pool_destroy(iterpool);
7485 return SVN_NO_ERROR;
7489 svn_wc__db_revert_list_done(svn_wc__db_t *db,
7490 const char *local_abspath,
7491 apr_pool_t *scratch_pool)
7493 svn_wc__db_wcroot_t *wcroot;
7494 const char *local_relpath;
7496 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7497 db, local_abspath, scratch_pool, scratch_pool));
7498 VERIFY_USABLE_WCROOT(wcroot);
7500 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_DROP_REVERT_LIST));
7502 return SVN_NO_ERROR;
7505 /* The body of svn_wc__db_op_remove_node().
7507 static svn_error_t *
7508 remove_node_txn(svn_boolean_t *left_changes,
7509 svn_wc__db_wcroot_t *wcroot,
7510 const char *local_relpath,
7512 svn_boolean_t destroy_wc,
7513 svn_boolean_t destroy_changes,
7514 const svn_skel_t *conflict,
7515 const svn_skel_t *work_items,
7516 svn_cancel_func_t cancel_func,
7518 apr_pool_t *scratch_pool)
7520 svn_sqlite__stmt_t *stmt;
7522 /* Note that unlike many similar functions it is a valid scenario for this
7523 function to be called on a wcroot! */
7525 /* db set when destroying wc */
7526 SVN_ERR_ASSERT(!destroy_wc || db != NULL);
7529 *left_changes = FALSE;
7532 && (!destroy_changes || *local_relpath == '\0'))
7534 svn_boolean_t have_row;
7535 apr_pool_t *iterpool;
7536 svn_error_t *err = NULL;
7538 /* Install WQ items for deleting the unmodified files and all dirs */
7539 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7540 STMT_SELECT_WORKING_PRESENT));
7541 SVN_ERR(svn_sqlite__bindf(stmt, "is",
7542 wcroot->wc_id, local_relpath));
7544 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7546 iterpool = svn_pool_create(scratch_pool);
7550 const char *child_relpath;
7551 const char *child_abspath;
7552 svn_node_kind_t child_kind;
7553 svn_boolean_t have_checksum;
7554 svn_filesize_t recorded_size;
7555 apr_int64_t recorded_time;
7556 const svn_io_dirent2_t *dirent;
7557 svn_boolean_t modified_p = TRUE;
7558 svn_skel_t *work_item = NULL;
7560 svn_pool_clear(iterpool);
7562 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7563 child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
7565 child_abspath = svn_dirent_join(wcroot->abspath, child_relpath,
7568 if (child_kind == svn_node_file)
7570 have_checksum = !svn_sqlite__column_is_null(stmt, 2);
7571 recorded_size = get_recorded_size(stmt, 3);
7572 recorded_time = svn_sqlite__column_int64(stmt, 4);
7576 err = cancel_func(cancel_baton);
7581 err = svn_io_stat_dirent2(&dirent, child_abspath, FALSE, TRUE,
7582 iterpool, iterpool);
7588 || dirent->kind != svn_node_file
7589 || child_kind != svn_node_file)
7591 /* Not interested in keeping changes */
7594 else if (child_kind == svn_node_file
7595 && dirent->kind == svn_node_file
7596 && dirent->filesize == recorded_size
7597 && dirent->mtime == recorded_time)
7599 modified_p = FALSE; /* File matches recorded state */
7601 else if (have_checksum)
7602 err = svn_wc__internal_file_modified_p(&modified_p,
7612 *left_changes = TRUE;
7614 else if (child_kind == svn_node_dir)
7616 err = svn_wc__wq_build_dir_remove(&work_item,
7617 db, wcroot->abspath,
7618 child_abspath, FALSE,
7619 iterpool, iterpool);
7621 else /* svn_node_file || svn_node_symlink */
7623 err = svn_wc__wq_build_file_remove(&work_item,
7624 db, wcroot->abspath,
7626 iterpool, iterpool);
7634 err = add_work_items(wcroot->sdb, work_item, iterpool);
7639 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7641 svn_pool_destroy(iterpool);
7643 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
7646 if (destroy_wc && *local_relpath != '\0')
7648 /* Create work item for destroying the root */
7649 svn_wc__db_status_t status;
7650 svn_node_kind_t kind;
7651 SVN_ERR(read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, NULL,
7652 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7653 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7654 wcroot, local_relpath,
7655 scratch_pool, scratch_pool));
7657 if (status == svn_wc__db_status_normal
7658 || status == svn_wc__db_status_added
7659 || status == svn_wc__db_status_incomplete)
7661 svn_skel_t *work_item = NULL;
7662 const char *local_abspath = svn_dirent_join(wcroot->abspath,
7666 if (kind == svn_node_dir)
7668 SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
7669 db, wcroot->abspath,
7673 scratch_pool, scratch_pool));
7677 svn_boolean_t modified_p = FALSE;
7679 if (!destroy_changes)
7681 SVN_ERR(svn_wc__internal_file_modified_p(&modified_p,
7688 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
7689 db, wcroot->abspath,
7696 *left_changes = TRUE;
7700 SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
7704 /* Remove all nodes below local_relpath */
7705 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7706 STMT_DELETE_NODE_RECURSIVE));
7707 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7708 SVN_ERR(svn_sqlite__step_done(stmt));
7710 /* Delete the root NODE when this is not the working copy root */
7711 if (local_relpath[0] != '\0')
7713 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7714 STMT_DELETE_NODE_ALL_LAYERS));
7715 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7716 SVN_ERR(svn_sqlite__step_done(stmt));
7719 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7720 STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7722 /* Delete all actual nodes at or below local_relpath */
7723 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7725 SVN_ERR(svn_sqlite__step_done(stmt));
7727 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
7729 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
7730 conflict, scratch_pool));
7732 return SVN_NO_ERROR;
7736 svn_wc__db_op_remove_node(svn_boolean_t *left_changes,
7738 const char *local_abspath,
7739 svn_boolean_t destroy_wc,
7740 svn_boolean_t destroy_changes,
7741 const svn_skel_t *conflict,
7742 const svn_skel_t *work_items,
7743 svn_cancel_func_t cancel_func,
7745 apr_pool_t *scratch_pool)
7747 svn_wc__db_wcroot_t *wcroot;
7748 const char *local_relpath;
7750 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7752 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7753 local_abspath, scratch_pool, scratch_pool));
7754 VERIFY_USABLE_WCROOT(wcroot);
7756 SVN_WC__DB_WITH_TXN(remove_node_txn(left_changes,
7757 wcroot, local_relpath, db,
7758 destroy_wc, destroy_changes,
7759 conflict, work_items,
7760 cancel_func, cancel_baton, scratch_pool),
7763 /* Flush everything below this node in all ways */
7764 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
7767 return SVN_NO_ERROR;
7771 /* The body of svn_wc__db_op_set_base_depth().
7773 static svn_error_t *
7774 db_op_set_base_depth(svn_wc__db_wcroot_t *wcroot,
7775 const char *local_relpath,
7777 apr_pool_t *scratch_pool)
7779 svn_sqlite__stmt_t *stmt;
7782 /* Flush any entries before we start monkeying the database. */
7783 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7784 STMT_UPDATE_NODE_BASE_DEPTH));
7785 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
7786 svn_token__to_word(depth_map, depth)));
7787 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7789 if (affected_rows == 0)
7790 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7791 _("The node '%s' is not a committed directory"),
7792 path_for_error_message(wcroot, local_relpath,
7795 return SVN_NO_ERROR;
7800 svn_wc__db_op_set_base_depth(svn_wc__db_t *db,
7801 const char *local_abspath,
7803 apr_pool_t *scratch_pool)
7805 svn_wc__db_wcroot_t *wcroot;
7806 const char *local_relpath;
7808 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7809 SVN_ERR_ASSERT(depth >= svn_depth_empty && depth <= svn_depth_infinity);
7811 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7812 local_abspath, scratch_pool, scratch_pool));
7813 VERIFY_USABLE_WCROOT(wcroot);
7815 /* ### We set depth on working and base to match entry behavior.
7816 Maybe these should be separated later? */
7817 SVN_WC__DB_WITH_TXN(db_op_set_base_depth(wcroot, local_relpath, depth,
7821 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
7823 return SVN_NO_ERROR;
7827 static svn_error_t *
7828 info_below_working(svn_boolean_t *have_base,
7829 svn_boolean_t *have_work,
7830 svn_wc__db_status_t *status,
7831 svn_wc__db_wcroot_t *wcroot,
7832 const char *local_relpath,
7833 int below_op_depth, /* < 0 is ignored */
7834 apr_pool_t *scratch_pool);
7837 /* Convert STATUS, the raw status obtained from the presence map, to
7838 the status appropriate for a working (op_depth > 0) node and return
7839 it in *WORKING_STATUS. */
7840 static svn_error_t *
7841 convert_to_working_status(svn_wc__db_status_t *working_status,
7842 svn_wc__db_status_t status)
7844 svn_wc__db_status_t work_status = status;
7846 SVN_ERR_ASSERT(work_status == svn_wc__db_status_normal
7847 || work_status == svn_wc__db_status_not_present
7848 || work_status == svn_wc__db_status_base_deleted
7849 || work_status == svn_wc__db_status_incomplete
7850 || work_status == svn_wc__db_status_excluded);
7852 if (work_status == svn_wc__db_status_excluded)
7854 *working_status = svn_wc__db_status_excluded;
7856 else if (work_status == svn_wc__db_status_not_present
7857 || work_status == svn_wc__db_status_base_deleted)
7859 /* The caller should scan upwards to detect whether this
7860 deletion has occurred because this node has been moved
7861 away, or it is a regular deletion. Also note that the
7862 deletion could be of the BASE tree, or a child of
7863 something that has been copied/moved here. */
7865 *working_status = svn_wc__db_status_deleted;
7867 else /* normal or incomplete */
7869 /* The caller should scan upwards to detect whether this
7870 addition has occurred because of a simple addition,
7871 a copy, or is the destination of a move. */
7872 *working_status = svn_wc__db_status_added;
7875 return SVN_NO_ERROR;
7879 /* Return the status of the node, if any, below the "working" node (or
7880 below BELOW_OP_DEPTH if >= 0).
7881 Set *HAVE_BASE or *HAVE_WORK to indicate if a base node or lower
7882 working node is present, and *STATUS to the status of the first
7883 layer below the selected node. */
7884 static svn_error_t *
7885 info_below_working(svn_boolean_t *have_base,
7886 svn_boolean_t *have_work,
7887 svn_wc__db_status_t *status,
7888 svn_wc__db_wcroot_t *wcroot,
7889 const char *local_relpath,
7891 apr_pool_t *scratch_pool)
7893 svn_sqlite__stmt_t *stmt;
7894 svn_boolean_t have_row;
7896 *have_base = *have_work = FALSE;
7897 *status = svn_wc__db_status_normal;
7899 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7900 STMT_SELECT_NODE_INFO));
7901 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7902 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7904 if (below_op_depth >= 0)
7907 (svn_sqlite__column_int(stmt, 0) > below_op_depth))
7909 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7914 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7916 *status = svn_sqlite__column_token(stmt, 3, presence_map);
7920 int op_depth = svn_sqlite__column_int(stmt, 0);
7927 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7930 SVN_ERR(svn_sqlite__reset(stmt));
7933 SVN_ERR(convert_to_working_status(status, *status));
7935 return SVN_NO_ERROR;
7938 /* Helper function for op_delete_txn */
7939 static svn_error_t *
7940 delete_update_movedto(svn_wc__db_wcroot_t *wcroot,
7941 const char *child_moved_from_relpath,
7943 const char *new_moved_to_relpath,
7944 apr_pool_t *scratch_pool)
7946 svn_sqlite__stmt_t *stmt;
7949 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7950 STMT_UPDATE_MOVED_TO_RELPATH));
7952 SVN_ERR(svn_sqlite__bindf(stmt, "isds",
7954 child_moved_from_relpath,
7956 new_moved_to_relpath));
7957 SVN_ERR(svn_sqlite__update(&affected, stmt));
7959 /* Not fatal in release mode. The move recording is broken,
7960 but the rest of the working copy can handle this. */
7961 SVN_ERR_ASSERT(affected == 1);
7964 return SVN_NO_ERROR;
7968 struct op_delete_baton_t {
7969 const char *moved_to_relpath; /* NULL if delete is not part of a move */
7970 svn_skel_t *conflict;
7971 svn_skel_t *work_items;
7972 svn_boolean_t delete_dir_externals;
7973 svn_boolean_t notify;
7976 /* This structure is used while rewriting move information for nodes.
7978 * The most simple case of rewriting move information happens when
7979 * a moved-away subtree is moved again: mv A B; mv B C
7980 * The second move requires rewriting moved-to info at or within A.
7982 * Another example is a move of a subtree which had nodes moved into it:
7984 * This requires rewriting such that A/F is marked has having moved to G/F.
7986 * Another case is where a node becomes a nested moved node.
7987 * A nested move happens when a subtree child is moved before or after
7988 * the subtree itself is moved. For example:
7989 * mv A/F A/G; mv A B
7990 * In this case, the move A/F -> A/G is rewritten to B/F -> B/G.
7991 * Note that the following sequence results in the same DB state:
7992 * mv A B; mv B/F B/G
7993 * We do not care about the order the moves were performed in.
7994 * For details, see http://wiki.apache.org/subversion/MultiLayerMoves
7996 struct moved_node_t {
7997 /* The source of the move. */
7998 const char *local_relpath;
8000 /* The move destination. */
8001 const char *moved_to_relpath;
8003 /* The op-depth of the deleted node at the source of the move. */
8006 /* When >= 1 the op_depth at which local_relpath was moved to its
8007 location. Used to find its original location outside the delete */
8008 int moved_from_depth;
8011 /* Helper function to resolve the original location of local_relpath at OP_DEPTH
8012 before it was moved into the tree rooted at ROOT_RELPATH. */
8013 static svn_error_t *
8014 resolve_moved_from(const char **moved_from_relpath,
8015 int *moved_from_op_depth,
8016 svn_wc__db_wcroot_t *wcroot,
8017 const char *root_relpath,
8018 const char *local_relpath,
8020 apr_pool_t *result_pool,
8021 apr_pool_t *scratch_pool)
8023 const char *suffix = "";
8024 svn_sqlite__stmt_t *stmt;
8025 const char *m_from_relpath;
8026 int m_from_op_depth;
8027 int m_move_from_depth;
8028 svn_boolean_t have_row;
8030 while (relpath_depth(local_relpath) > op_depth)
8033 svn_relpath_split(&local_relpath, &name, local_relpath, scratch_pool);
8034 suffix = svn_relpath_join(suffix, name, scratch_pool);
8037 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8038 STMT_SELECT_MOVED_FROM_FOR_DELETE));
8039 SVN_ERR(svn_sqlite__bindf(stmt, "is",
8040 wcroot->wc_id, local_relpath));
8041 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8045 /* assert(have_row); */
8046 *moved_from_relpath = NULL;
8047 *moved_from_op_depth = -1;
8049 SVN_ERR(svn_sqlite__reset(stmt));
8051 return SVN_NO_ERROR;
8054 m_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
8055 m_from_op_depth = svn_sqlite__column_int(stmt, 1);
8056 m_move_from_depth = svn_sqlite__column_int(stmt, 2);
8058 SVN_ERR(svn_sqlite__reset(stmt));
8060 if (! svn_relpath_skip_ancestor(root_relpath, m_from_relpath))
8062 *moved_from_relpath = svn_relpath_join(m_from_relpath, suffix,
8064 *moved_from_op_depth = m_from_op_depth; /* ### Ok? */
8065 return SVN_NO_ERROR;
8067 else if (!m_move_from_depth)
8069 *moved_from_relpath = NULL;
8070 *moved_from_op_depth = -1;
8071 return SVN_NO_ERROR;
8074 return svn_error_trace(
8075 resolve_moved_from(moved_from_relpath,
8076 moved_from_op_depth,
8079 svn_relpath_join(m_from_relpath, suffix,
8082 result_pool, scratch_pool));
8085 static svn_error_t *
8086 delete_node(void *baton,
8087 svn_wc__db_wcroot_t *wcroot,
8088 const char *local_relpath,
8089 apr_pool_t *scratch_pool)
8091 struct op_delete_baton_t *b = baton;
8092 svn_wc__db_status_t status;
8093 svn_boolean_t have_row, op_root;
8094 svn_boolean_t add_work = FALSE;
8095 svn_sqlite__stmt_t *stmt;
8096 int working_op_depth; /* Depth of what is to be deleted */
8097 int keep_op_depth = 0; /* Depth of what is below what is deleted */
8098 svn_node_kind_t kind;
8099 apr_array_header_t *moved_nodes = NULL;
8100 int delete_op_depth = relpath_depth(local_relpath);
8102 assert(*local_relpath); /* Can't delete wcroot */
8104 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8105 STMT_SELECT_NODE_INFO));
8106 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8107 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8111 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
8112 svn_sqlite__reset(stmt),
8113 _("The node '%s' was not found."),
8114 path_for_error_message(wcroot,
8119 working_op_depth = svn_sqlite__column_int(stmt, 0);
8120 status = svn_sqlite__column_token(stmt, 3, presence_map);
8121 kind = svn_sqlite__column_token(stmt, 4, kind_map);
8123 if (working_op_depth < delete_op_depth)
8127 keep_op_depth = working_op_depth;
8133 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8137 svn_wc__db_status_t below_status;
8140 below_op_depth = svn_sqlite__column_int(stmt, 0);
8141 below_status = svn_sqlite__column_token(stmt, 3, presence_map);
8143 if (below_status != svn_wc__db_status_not_present
8144 && below_status != svn_wc__db_status_base_deleted)
8147 keep_op_depth = below_op_depth;
8156 SVN_ERR(svn_sqlite__reset(stmt));
8158 if (working_op_depth != 0) /* WORKING */
8159 SVN_ERR(convert_to_working_status(&status, status));
8161 if (status == svn_wc__db_status_deleted
8162 || status == svn_wc__db_status_not_present)
8163 return SVN_NO_ERROR;
8165 /* Don't copy BASE directories with server excluded nodes */
8166 if (status == svn_wc__db_status_normal && kind == svn_node_dir)
8168 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8169 STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
8170 SVN_ERR(svn_sqlite__bindf(stmt, "is",
8171 wcroot->wc_id, local_relpath));
8172 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8175 const char *absent_path = svn_sqlite__column_text(stmt, 0,
8178 return svn_error_createf(
8179 SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
8180 svn_sqlite__reset(stmt),
8181 _("Cannot delete '%s' as '%s' is excluded by server"),
8182 path_for_error_message(wcroot, local_relpath,
8184 path_for_error_message(wcroot, absent_path,
8187 SVN_ERR(svn_sqlite__reset(stmt));
8189 else if (status == svn_wc__db_status_server_excluded)
8191 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
8192 _("Cannot delete '%s' as it is excluded by server"),
8193 path_for_error_message(wcroot, local_relpath,
8196 else if (status == svn_wc__db_status_excluded)
8198 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
8199 _("Cannot delete '%s' as it is excluded"),
8200 path_for_error_message(wcroot, local_relpath,
8204 if (b->moved_to_relpath)
8206 const char *moved_from_relpath = NULL;
8207 struct moved_node_t *moved_node;
8210 moved_nodes = apr_array_make(scratch_pool, 1,
8211 sizeof(struct moved_node_t *));
8213 /* The node is being moved-away.
8214 * Figure out if the node was moved-here before, or whether this
8215 * is the first time the node is moved. */
8216 if (status == svn_wc__db_status_added)
8217 SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL,
8218 &moved_from_relpath,
8221 wcroot, local_relpath,
8222 scratch_pool, scratch_pool));
8224 if (op_root && moved_from_relpath)
8226 const char *part = svn_relpath_skip_ancestor(local_relpath,
8227 moved_from_relpath);
8229 /* Existing move-root is moved to another location */
8230 moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
8232 moved_node->local_relpath = moved_from_relpath;
8234 moved_node->local_relpath = svn_relpath_join(b->moved_to_relpath,
8235 part, scratch_pool);
8236 moved_node->op_depth = move_op_depth;
8237 moved_node->moved_to_relpath = b->moved_to_relpath;
8238 moved_node->moved_from_depth = -1;
8240 APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
8242 else if (!op_root && (status == svn_wc__db_status_normal
8243 || status == svn_wc__db_status_copied
8244 || status == svn_wc__db_status_moved_here))
8246 /* The node is becoming a move-root for the first time,
8247 * possibly because of a nested move operation. */
8248 moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
8249 moved_node->local_relpath = local_relpath;
8250 moved_node->op_depth = delete_op_depth;
8251 moved_node->moved_to_relpath = b->moved_to_relpath;
8252 moved_node->moved_from_depth = -1;
8254 APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
8256 /* Else: We can't track history of local additions and/or of things we are
8259 /* And update all moved_to values still pointing to this location */
8260 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8261 STMT_UPDATE_MOVED_TO_DESCENDANTS));
8262 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
8264 b->moved_to_relpath));
8265 SVN_ERR(svn_sqlite__update(NULL, stmt));
8268 /* Find children that were moved out of the subtree rooted at this node.
8269 * We'll need to update their op-depth columns because their deletion
8270 * is now implied by the deletion of their parent (i.e. this node). */
8272 apr_pool_t *iterpool;
8275 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8276 STMT_SELECT_MOVED_FOR_DELETE));
8277 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
8280 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8281 iterpool = svn_pool_create(scratch_pool);
8284 struct moved_node_t *mn;
8285 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8286 const char *mv_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
8287 int child_op_depth = svn_sqlite__column_int(stmt, 2);
8288 int moved_from_depth = -1;
8289 svn_boolean_t fixup = FALSE;
8291 if (! b->moved_to_relpath
8292 && ! svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
8294 /* a NULL moved_here_depth will be reported as 0 */
8295 int moved_here_depth = svn_sqlite__column_int(stmt, 3);
8297 /* Plain delete. Fixup move information of descendants that were
8298 moved here, or that were moved out */
8300 if (moved_here_depth >= delete_op_depth)
8302 /* The move we recorded here must be moved to the location
8303 this node had before it was moved here.
8305 This might contain multiple steps when the node was moved
8306 in several places within the to be deleted tree */
8308 /* ### TODO: Add logic */
8310 moved_from_depth = moved_here_depth;
8314 /* Update the op-depth of an moved away node that was
8315 registered as moved by the records that we are about
8318 child_op_depth = delete_op_depth;
8321 else if (b->moved_to_relpath)
8323 /* The node is moved to a new location */
8325 if (delete_op_depth == child_op_depth)
8327 /* Update the op-depth of a tree shadowed by this tree */
8329 /*child_op_depth = delete_depth;*/
8331 else if (child_op_depth >= delete_op_depth
8332 && !svn_relpath_skip_ancestor(local_relpath,
8335 /* Update the move destination of something that is now moved
8338 child_relpath = svn_relpath_skip_ancestor(local_relpath,
8343 child_relpath = svn_relpath_join(b->moved_to_relpath,
8347 if (child_op_depth > delete_op_depth
8348 && svn_relpath_skip_ancestor(local_relpath,
8350 child_op_depth = delete_op_depth;
8353 /* Calculate depth of the shadowing at the new location */
8354 child_op_depth = child_op_depth
8355 - relpath_depth(local_relpath)
8356 + relpath_depth(b->moved_to_relpath);
8366 mn = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
8368 mn->local_relpath = apr_pstrdup(scratch_pool, child_relpath);
8369 mn->moved_to_relpath = apr_pstrdup(scratch_pool, mv_to_relpath);
8370 mn->op_depth = child_op_depth;
8371 mn->moved_from_depth = moved_from_depth;
8374 moved_nodes = apr_array_make(scratch_pool, 1,
8375 sizeof(struct moved_node_t *));
8376 APR_ARRAY_PUSH(moved_nodes, struct moved_node_t *) = mn;
8379 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8381 SVN_ERR(svn_sqlite__reset(stmt));
8383 for (i = 0; moved_nodes && (i < moved_nodes->nelts); i++)
8385 struct moved_node_t *mn = APR_ARRAY_IDX(moved_nodes, i,
8386 struct moved_node_t *);
8388 if (mn->moved_from_depth > 0)
8390 svn_pool_clear(iterpool);
8392 SVN_ERR(resolve_moved_from(&mn->local_relpath, &mn->op_depth,
8393 wcroot, local_relpath,
8395 mn->moved_from_depth,
8396 scratch_pool, iterpool));
8398 if (!mn->local_relpath)
8399 svn_sort__array_delete(moved_nodes, i--, 1);
8403 svn_pool_destroy(iterpool);
8406 if (!b->moved_to_relpath)
8408 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8409 STMT_CLEAR_MOVED_TO_DESCENDANTS));
8410 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8412 SVN_ERR(svn_sqlite__update(NULL, stmt));
8416 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8417 STMT_CLEAR_MOVED_TO_FROM_DEST));
8418 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8421 SVN_ERR(svn_sqlite__update(NULL, stmt));
8426 /* ### Put actual-only nodes into the list? */
8429 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8430 STMT_INSERT_DELETE_LIST));
8431 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
8432 wcroot->wc_id, local_relpath, working_op_depth));
8433 SVN_ERR(svn_sqlite__step_done(stmt));
8436 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8437 STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
8438 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
8439 wcroot->wc_id, local_relpath, delete_op_depth));
8440 SVN_ERR(svn_sqlite__step_done(stmt));
8442 /* Delete ACTUAL_NODE rows, but leave those that have changelist
8444 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8445 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8446 SVN_ERR(svn_sqlite__bindf(stmt, "is",
8447 wcroot->wc_id, local_relpath));
8448 SVN_ERR(svn_sqlite__step_done(stmt));
8450 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8451 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8452 SVN_ERR(svn_sqlite__bindf(stmt, "is",
8453 wcroot->wc_id, local_relpath));
8454 SVN_ERR(svn_sqlite__step_done(stmt));
8456 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8457 STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
8458 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8460 SVN_ERR(svn_sqlite__step_done(stmt));
8464 /* Delete the node at LOCAL_RELPATH, and possibly mark it as moved. */
8466 /* Delete the node and possible descendants. */
8467 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8468 STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
8469 SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
8470 wcroot->wc_id, local_relpath,
8471 keep_op_depth, delete_op_depth));
8472 SVN_ERR(svn_sqlite__step_done(stmt));
8479 for (i = 0; i < moved_nodes->nelts; ++i)
8481 const struct moved_node_t *moved_node
8482 = APR_ARRAY_IDX(moved_nodes, i, void *);
8484 SVN_ERR(delete_update_movedto(wcroot,
8485 moved_node->local_relpath,
8486 moved_node->op_depth,
8487 moved_node->moved_to_relpath,
8492 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8493 STMT_DELETE_FILE_EXTERNALS));
8494 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8495 SVN_ERR(svn_sqlite__step_done(stmt));
8497 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8498 b->delete_dir_externals
8499 ? STMT_DELETE_EXTERNAL_REGISTATIONS
8500 : STMT_DELETE_FILE_EXTERNAL_REGISTATIONS));
8501 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8502 SVN_ERR(svn_sqlite__step_done(stmt));
8504 SVN_ERR(add_work_items(wcroot->sdb, b->work_items, scratch_pool));
8506 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
8507 b->conflict, scratch_pool));
8509 return SVN_NO_ERROR;
8512 static svn_error_t *
8513 op_delete_txn(void *baton,
8514 svn_wc__db_wcroot_t *wcroot,
8515 const char *local_relpath,
8516 apr_pool_t *scratch_pool)
8519 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8520 SVN_ERR(delete_node(baton, wcroot, local_relpath, scratch_pool));
8521 return SVN_NO_ERROR;
8525 struct op_delete_many_baton_t {
8526 apr_array_header_t *rel_targets;
8527 svn_boolean_t delete_dir_externals;
8528 const svn_skel_t *work_items;
8531 static svn_error_t *
8532 op_delete_many_txn(void *baton,
8533 svn_wc__db_wcroot_t *wcroot,
8534 const char *local_relpath,
8535 apr_pool_t *scratch_pool)
8537 struct op_delete_many_baton_t *odmb = baton;
8538 struct op_delete_baton_t odb;
8540 apr_pool_t *iterpool;
8542 odb.moved_to_relpath = NULL;
8543 odb.conflict = NULL;
8544 odb.work_items = NULL;
8545 odb.delete_dir_externals = odmb->delete_dir_externals;
8548 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8549 iterpool = svn_pool_create(scratch_pool);
8550 for (i = 0; i < odmb->rel_targets->nelts; i++)
8552 const char *target_relpath = APR_ARRAY_IDX(odmb->rel_targets, i,
8556 svn_pool_clear(iterpool);
8557 SVN_ERR(delete_node(&odb, wcroot, target_relpath, iterpool));
8559 svn_pool_destroy(iterpool);
8561 SVN_ERR(add_work_items(wcroot->sdb, odmb->work_items, scratch_pool));
8563 return SVN_NO_ERROR;
8567 static svn_error_t *
8568 do_delete_notify(void *baton,
8569 svn_wc__db_wcroot_t *wcroot,
8570 svn_cancel_func_t cancel_func,
8572 svn_wc_notify_func2_t notify_func,
8574 apr_pool_t *scratch_pool)
8576 svn_sqlite__stmt_t *stmt;
8577 svn_boolean_t have_row;
8578 apr_pool_t *iterpool;
8580 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8581 STMT_SELECT_DELETE_LIST));
8582 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8584 iterpool = svn_pool_create(scratch_pool);
8587 const char *notify_relpath;
8588 const char *notify_abspath;
8590 svn_pool_clear(iterpool);
8592 notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8593 notify_abspath = svn_dirent_join(wcroot->abspath,
8597 notify_func(notify_baton,
8598 svn_wc_create_notify(notify_abspath,
8599 svn_wc_notify_delete,
8603 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8605 svn_pool_destroy(iterpool);
8607 SVN_ERR(svn_sqlite__reset(stmt));
8609 /* We only allow cancellation after notification for all deleted nodes
8610 * has happened. The nodes are already deleted so we should notify for
8613 SVN_ERR(cancel_func(cancel_baton));
8615 return SVN_NO_ERROR;
8620 svn_wc__db_op_delete(svn_wc__db_t *db,
8621 const char *local_abspath,
8622 const char *moved_to_abspath,
8623 svn_boolean_t delete_dir_externals,
8624 svn_skel_t *conflict,
8625 svn_skel_t *work_items,
8626 svn_cancel_func_t cancel_func,
8628 svn_wc_notify_func2_t notify_func,
8630 apr_pool_t *scratch_pool)
8632 svn_wc__db_wcroot_t *wcroot;
8633 svn_wc__db_wcroot_t *moved_to_wcroot;
8634 const char *local_relpath;
8635 const char *moved_to_relpath;
8636 struct op_delete_baton_t odb;
8638 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8640 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8642 scratch_pool, scratch_pool));
8643 VERIFY_USABLE_WCROOT(wcroot);
8645 if (moved_to_abspath)
8647 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&moved_to_wcroot,
8649 db, moved_to_abspath,
8652 VERIFY_USABLE_WCROOT(moved_to_wcroot);
8654 if (strcmp(wcroot->abspath, moved_to_wcroot->abspath) != 0)
8655 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
8656 _("Cannot move '%s' to '%s' because they "
8657 "are not in the same working copy"),
8658 svn_dirent_local_style(local_abspath,
8660 svn_dirent_local_style(moved_to_abspath,
8664 moved_to_relpath = NULL;
8666 odb.moved_to_relpath = moved_to_relpath;
8667 odb.conflict = conflict;
8668 odb.work_items = work_items;
8669 odb.delete_dir_externals = delete_dir_externals;
8673 /* Perform the deletion operation (transactionally), perform any
8674 notifications necessary, and then clean out our temporary tables. */
8676 SVN_ERR(with_finalization(wcroot, local_relpath,
8677 op_delete_txn, &odb,
8678 do_delete_notify, NULL,
8679 cancel_func, cancel_baton,
8680 notify_func, notify_baton,
8681 STMT_FINALIZE_DELETE,
8686 /* Avoid the trigger work */
8688 SVN_WC__DB_WITH_TXN(
8689 delete_node(&odb, wcroot, local_relpath, scratch_pool),
8693 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
8696 return SVN_NO_ERROR;
8701 svn_wc__db_op_delete_many(svn_wc__db_t *db,
8702 apr_array_header_t *targets,
8703 svn_boolean_t delete_dir_externals,
8704 const svn_skel_t *work_items,
8705 svn_cancel_func_t cancel_func,
8707 svn_wc_notify_func2_t notify_func,
8709 apr_pool_t *scratch_pool)
8711 svn_wc__db_wcroot_t *wcroot;
8712 const char *local_relpath;
8713 struct op_delete_many_baton_t odmb;
8715 apr_pool_t *iterpool;
8717 odmb.rel_targets = apr_array_make(scratch_pool, targets->nelts,
8718 sizeof(const char *));
8719 odmb.work_items = work_items;
8720 odmb.delete_dir_externals = delete_dir_externals;
8721 iterpool = svn_pool_create(scratch_pool);
8722 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8724 APR_ARRAY_IDX(targets, 0,
8726 scratch_pool, iterpool));
8727 VERIFY_USABLE_WCROOT(wcroot);
8728 for (i = 0; i < targets->nelts; i++)
8730 const char *local_abspath = APR_ARRAY_IDX(targets, i, const char*);
8731 svn_wc__db_wcroot_t *target_wcroot;
8733 svn_pool_clear(iterpool);
8735 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&target_wcroot,
8737 APR_ARRAY_IDX(targets, i,
8739 scratch_pool, iterpool));
8740 VERIFY_USABLE_WCROOT(target_wcroot);
8741 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8743 /* Assert that all targets are within the same working copy. */
8744 SVN_ERR_ASSERT(wcroot->wc_id == target_wcroot->wc_id);
8746 APR_ARRAY_PUSH(odmb.rel_targets, const char *) = local_relpath;
8747 SVN_ERR(flush_entries(target_wcroot, local_abspath, svn_depth_infinity,
8751 svn_pool_destroy(iterpool);
8753 /* Perform the deletion operation (transactionally), perform any
8754 notifications necessary, and then clean out our temporary tables. */
8755 return svn_error_trace(with_finalization(wcroot, wcroot->abspath,
8756 op_delete_many_txn, &odmb,
8757 do_delete_notify, NULL,
8758 cancel_func, cancel_baton,
8759 notify_func, notify_baton,
8760 STMT_FINALIZE_DELETE,
8764 /* Helper function for read_info() to provide better diagnostics than just
8767 ### BH: Yes this code is ugly, and that is why I only introduce it in
8768 ### read_info(). But we really need something to determine the root cause
8769 ### of this problem to diagnose why TortoiseSVN users were seeing all those
8772 Adds an error to the *err chain if invalid values are encountered. In that
8773 case the value is set to the first value in the map, assuming that caller
8774 will just return the combined error.
8777 column_token_err(svn_error_t **err,
8778 svn_sqlite__stmt_t *stmt,
8780 const svn_token_map_t *map)
8783 const char *word = svn_sqlite__column_text(stmt, column, NULL);
8786 /* svn_token__from_word_err() handles NULL for us */
8787 err2 = svn_token__from_word_err(&value, map, word);
8791 *err = svn_error_compose_create(
8794 SVN_ERR_WC_CORRUPT, err2,
8795 _("Encountered invalid node state in column %d of "
8796 "info query to working copy database"),
8804 /* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of
8805 DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */
8806 static svn_error_t *
8807 read_info(svn_wc__db_status_t *status,
8808 svn_node_kind_t *kind,
8809 svn_revnum_t *revision,
8810 const char **repos_relpath,
8811 apr_int64_t *repos_id,
8812 svn_revnum_t *changed_rev,
8813 apr_time_t *changed_date,
8814 const char **changed_author,
8816 const svn_checksum_t **checksum,
8817 const char **target,
8818 const char **original_repos_relpath,
8819 apr_int64_t *original_repos_id,
8820 svn_revnum_t *original_revision,
8821 svn_wc__db_lock_t **lock,
8822 svn_filesize_t *recorded_size,
8823 apr_time_t *recorded_time,
8824 const char **changelist,
8825 svn_boolean_t *conflicted,
8826 svn_boolean_t *op_root,
8827 svn_boolean_t *had_props,
8828 svn_boolean_t *props_mod,
8829 svn_boolean_t *have_base,
8830 svn_boolean_t *have_more_work,
8831 svn_boolean_t *have_work,
8832 svn_wc__db_wcroot_t *wcroot,
8833 const char *local_relpath,
8834 apr_pool_t *result_pool,
8835 apr_pool_t *scratch_pool)
8837 svn_sqlite__stmt_t *stmt_info;
8838 svn_sqlite__stmt_t *stmt_act;
8839 svn_boolean_t have_info;
8840 svn_boolean_t have_act;
8841 svn_error_t *err = NULL;
8843 /* Obtain the most likely to exist record first, to make sure we don't
8844 have to obtain the SQLite read-lock multiple times */
8845 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
8846 lock ? STMT_SELECT_NODE_INFO_WITH_LOCK
8847 : STMT_SELECT_NODE_INFO));
8848 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
8849 SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
8851 if (changelist || conflicted || props_mod)
8853 SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
8854 STMT_SELECT_ACTUAL_NODE));
8855 SVN_ERR(svn_sqlite__bindf(stmt_act, "is", wcroot->wc_id, local_relpath));
8856 SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
8867 svn_node_kind_t node_kind;
8869 op_depth = svn_sqlite__column_int(stmt_info, 0);
8870 node_kind = column_token_err(&err, stmt_info, 4, kind_map);
8874 *status = column_token_err(&err, stmt_info, 3, presence_map);
8876 if (op_depth != 0) /* WORKING */
8877 err = svn_error_compose_create(err,
8878 convert_to_working_status(status,
8888 *repos_id = INVALID_REPOS_ID;
8890 *revision = SVN_INVALID_REVNUM;
8892 /* Our path is implied by our parent somewhere up the tree.
8893 With the NULL value and status, the caller will know to
8894 search up the tree for the base of our path. */
8895 *repos_relpath = NULL;
8899 /* Fetch repository information. If we have a
8900 WORKING_NODE (and have been added), then the repository
8901 we're being added to will be dependent upon a parent. The
8902 caller can scan upwards to locate the repository. */
8903 repos_location_from_columns(repos_id, revision, repos_relpath,
8904 stmt_info, 1, 5, 2, result_pool);
8908 *changed_rev = svn_sqlite__column_revnum(stmt_info, 8);
8912 *changed_date = svn_sqlite__column_int64(stmt_info, 9);
8916 *changed_author = svn_sqlite__column_text(stmt_info, 10,
8921 *recorded_time = svn_sqlite__column_int64(stmt_info, 13);
8925 if (node_kind != svn_node_dir)
8926 *depth = svn_depth_unknown;
8927 else if (svn_sqlite__column_is_null(stmt_info, 11))
8928 *depth = svn_depth_unknown;
8930 *depth = column_token_err(&err, stmt_info, 11, depth_map);
8934 if (node_kind != svn_node_file)
8941 err = svn_error_compose_create(
8942 err, svn_sqlite__column_checksum(checksum, stmt_info, 6,
8948 *recorded_size = get_recorded_size(stmt_info, 7);
8952 if (node_kind != svn_node_symlink)
8955 *target = svn_sqlite__column_text(stmt_info, 12, result_pool);
8960 *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8966 if (original_repos_id)
8967 *original_repos_id = INVALID_REPOS_ID;
8968 if (original_revision)
8969 *original_revision = SVN_INVALID_REVNUM;
8970 if (original_repos_relpath)
8971 *original_repos_relpath = NULL;
8975 repos_location_from_columns(original_repos_id,
8977 original_repos_relpath,
8978 stmt_info, 1, 5, 2, result_pool);
8982 *props_mod = have_act && !svn_sqlite__column_is_null(stmt_act, 1);
8986 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt_info, 14);
8993 !svn_sqlite__column_is_null(stmt_act, 2); /* conflict_data */
8996 *conflicted = FALSE;
9004 *lock = lock_from_columns(stmt_info, 17, 18, 19, 20, result_pool);
9008 *have_work = (op_depth != 0);
9012 *op_root = ((op_depth > 0)
9013 && (op_depth == relpath_depth(local_relpath)));
9016 if (have_base || have_more_work)
9019 *have_more_work = FALSE;
9021 while (!err && op_depth != 0)
9023 err = svn_sqlite__step(&have_info, stmt_info);
9025 if (err || !have_info)
9028 op_depth = svn_sqlite__column_int(stmt_info, 0);
9033 *have_more_work = TRUE;
9041 *have_base = (op_depth == 0);
9046 /* A row in ACTUAL_NODE should never exist without a corresponding
9047 node in BASE_NODE and/or WORKING_NODE unless it flags a tree conflict. */
9048 if (svn_sqlite__column_is_null(stmt_act, 2)) /* conflict_data */
9049 err = svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
9050 _("Corrupt data for '%s'"),
9051 path_for_error_message(wcroot, local_relpath,
9053 /* ### What should we return? Should we have a separate
9054 function for reading actual-only nodes? */
9056 /* As a safety measure, until we decide if we want to use
9057 read_info for actual-only nodes, make sure the caller asked
9058 for the conflict status. */
9059 SVN_ERR_ASSERT(conflicted);
9062 *status = svn_wc__db_status_normal; /* What! No it's not! */
9064 *kind = svn_node_unknown;
9066 *revision = SVN_INVALID_REVNUM;
9068 *repos_relpath = NULL;
9070 *repos_id = INVALID_REPOS_ID;
9072 *changed_rev = SVN_INVALID_REVNUM;
9076 *depth = svn_depth_unknown;
9081 if (original_repos_relpath)
9082 *original_repos_relpath = NULL;
9083 if (original_repos_id)
9084 *original_repos_id = INVALID_REPOS_ID;
9085 if (original_revision)
9086 *original_revision = SVN_INVALID_REVNUM;
9094 *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
9106 *have_more_work = FALSE;
9112 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
9113 _("The node '%s' was not found."),
9114 path_for_error_message(wcroot, local_relpath,
9118 if (stmt_act != NULL)
9119 err = svn_error_compose_create(err, svn_sqlite__reset(stmt_act));
9121 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
9122 err = svn_error_quick_wrapf(err, _("Error reading node '%s'"),
9125 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info)));
9127 return SVN_NO_ERROR;
9132 svn_wc__db_read_info_internal(svn_wc__db_status_t *status,
9133 svn_node_kind_t *kind,
9134 svn_revnum_t *revision,
9135 const char **repos_relpath,
9136 apr_int64_t *repos_id,
9137 svn_revnum_t *changed_rev,
9138 apr_time_t *changed_date,
9139 const char **changed_author,
9141 const svn_checksum_t **checksum,
9142 const char **target,
9143 const char **original_repos_relpath,
9144 apr_int64_t *original_repos_id,
9145 svn_revnum_t *original_revision,
9146 svn_wc__db_lock_t **lock,
9147 svn_filesize_t *recorded_size,
9148 apr_time_t *recorded_time,
9149 const char **changelist,
9150 svn_boolean_t *conflicted,
9151 svn_boolean_t *op_root,
9152 svn_boolean_t *had_props,
9153 svn_boolean_t *props_mod,
9154 svn_boolean_t *have_base,
9155 svn_boolean_t *have_more_work,
9156 svn_boolean_t *have_work,
9157 svn_wc__db_wcroot_t *wcroot,
9158 const char *local_relpath,
9159 apr_pool_t *result_pool,
9160 apr_pool_t *scratch_pool)
9162 return svn_error_trace(
9163 read_info(status, kind, revision, repos_relpath, repos_id,
9164 changed_rev, changed_date, changed_author,
9165 depth, checksum, target, original_repos_relpath,
9166 original_repos_id, original_revision, lock,
9167 recorded_size, recorded_time, changelist, conflicted,
9168 op_root, had_props, props_mod,
9169 have_base, have_more_work, have_work,
9170 wcroot, local_relpath, result_pool, scratch_pool));
9175 svn_wc__db_read_info(svn_wc__db_status_t *status,
9176 svn_node_kind_t *kind,
9177 svn_revnum_t *revision,
9178 const char **repos_relpath,
9179 const char **repos_root_url,
9180 const char **repos_uuid,
9181 svn_revnum_t *changed_rev,
9182 apr_time_t *changed_date,
9183 const char **changed_author,
9185 const svn_checksum_t **checksum,
9186 const char **target,
9187 const char **original_repos_relpath,
9188 const char **original_root_url,
9189 const char **original_uuid,
9190 svn_revnum_t *original_revision,
9191 svn_wc__db_lock_t **lock,
9192 svn_filesize_t *recorded_size,
9193 apr_time_t *recorded_time,
9194 const char **changelist,
9195 svn_boolean_t *conflicted,
9196 svn_boolean_t *op_root,
9197 svn_boolean_t *have_props,
9198 svn_boolean_t *props_mod,
9199 svn_boolean_t *have_base,
9200 svn_boolean_t *have_more_work,
9201 svn_boolean_t *have_work,
9203 const char *local_abspath,
9204 apr_pool_t *result_pool,
9205 apr_pool_t *scratch_pool)
9207 svn_wc__db_wcroot_t *wcroot;
9208 const char *local_relpath;
9209 apr_int64_t repos_id, original_repos_id;
9211 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9213 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9214 local_abspath, scratch_pool, scratch_pool));
9215 VERIFY_USABLE_WCROOT(wcroot);
9217 SVN_WC__DB_WITH_TXN4(
9218 read_info(status, kind, revision, repos_relpath, &repos_id,
9219 changed_rev, changed_date, changed_author,
9220 depth, checksum, target, original_repos_relpath,
9221 &original_repos_id, original_revision, lock,
9222 recorded_size, recorded_time, changelist, conflicted,
9223 op_root, have_props, props_mod,
9224 have_base, have_more_work, have_work,
9225 wcroot, local_relpath, result_pool, scratch_pool),
9226 svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
9227 wcroot, repos_id, result_pool),
9228 svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
9229 wcroot, original_repos_id,
9234 return SVN_NO_ERROR;
9237 static svn_error_t *
9238 is_wclocked(svn_boolean_t *locked,
9239 svn_wc__db_wcroot_t *wcroot,
9240 const char *dir_relpath,
9241 apr_pool_t *scratch_pool);
9243 /* Helper for read_children_info and single variant */
9244 static svn_error_t *
9245 find_conflict_descendants(svn_boolean_t *conflict_exists,
9246 svn_wc__db_wcroot_t *wcroot,
9247 const char *local_relpath,
9248 apr_pool_t *scratch_pool)
9250 svn_sqlite__stmt_t *stmt;
9252 /* Only used on files, so certainly not wcroot*/
9253 assert(local_relpath[0] != '\0');
9255 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9256 STMT_FIND_CONFLICT_DESCENDANT));
9258 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9259 SVN_ERR(svn_sqlite__step(conflict_exists, stmt));
9261 return svn_error_trace(svn_sqlite__reset(stmt));
9264 /* What we really want to store about a node. This relies on the
9265 offset of svn_wc__db_info_t being zero. */
9266 struct read_children_info_item_t
9268 struct svn_wc__db_info_t info;
9271 svn_boolean_t was_dir;
9274 /* Implementation of svn_wc__db_read_children_info */
9275 static svn_error_t *
9276 read_children_info(svn_wc__db_wcroot_t *wcroot,
9277 const char *dir_relpath,
9278 apr_hash_t *conflicts,
9280 svn_boolean_t base_tree_only,
9281 apr_pool_t *result_pool,
9282 apr_pool_t *scratch_pool)
9284 svn_sqlite__stmt_t *stmt;
9285 svn_boolean_t have_row;
9286 const char *repos_root_url = NULL;
9287 const char *repos_uuid = NULL;
9288 apr_int64_t last_repos_id = INVALID_REPOS_ID;
9289 const char *last_repos_root_url = NULL;
9291 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9293 ? STMT_SELECT_BASE_NODE_CHILDREN_INFO
9294 : STMT_SELECT_NODE_CHILDREN_INFO)));
9295 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9296 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9300 /* CHILD item points to what we have about the node. We only provide
9301 CHILD->item to our caller. */
9302 struct read_children_info_item_t *child_item;
9303 const char *child_relpath = svn_sqlite__column_text(stmt, 19, NULL);
9304 const char *name = svn_relpath_basename(child_relpath, NULL);
9307 svn_boolean_t new_child;
9309 child_item = (base_tree_only ? NULL : svn_hash_gets(nodes, name));
9314 child_item = apr_pcalloc(result_pool, sizeof(*child_item));
9318 op_depth = svn_sqlite__column_int(stmt, 0);
9320 /* Do we have new or better information? */
9323 struct svn_wc__db_info_t *child = &child_item->info;
9324 child_item->op_depth = op_depth;
9326 child->kind = svn_sqlite__column_token(stmt, 4, kind_map);
9328 child->status = svn_sqlite__column_token(stmt, 3, presence_map);
9331 if (child->status == svn_wc__db_status_incomplete)
9332 child->incomplete = TRUE;
9333 err = convert_to_working_status(&child->status, child->status);
9335 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9339 child->revnum = SVN_INVALID_REVNUM;
9341 child->revnum = svn_sqlite__column_revnum(stmt, 5);
9344 child->repos_relpath = NULL;
9346 child->repos_relpath = svn_sqlite__column_text(stmt, 2,
9349 if (op_depth != 0 || svn_sqlite__column_is_null(stmt, 1))
9351 child->repos_root_url = NULL;
9352 child->repos_uuid = NULL;
9356 apr_int64_t repos_id = svn_sqlite__column_int64(stmt, 1);
9357 if (!repos_root_url ||
9358 (last_repos_id != INVALID_REPOS_ID &&
9359 repos_id != last_repos_id))
9361 last_repos_root_url = repos_root_url;
9362 err = svn_wc__db_fetch_repos_info(&repos_root_url,
9367 SVN_ERR(svn_error_compose_create(err,
9368 svn_sqlite__reset(stmt)));
9371 if (last_repos_id == INVALID_REPOS_ID)
9372 last_repos_id = repos_id;
9374 /* Assume working copy is all one repos_id so that a
9375 single cached value is sufficient. */
9376 if (repos_id != last_repos_id)
9378 err= svn_error_createf(
9379 SVN_ERR_WC_DB_ERROR, NULL,
9380 _("The node '%s' comes from unexpected repository "
9381 "'%s', expected '%s'; if this node is a file "
9382 "external using the correct URL in the external "
9383 "definition can fix the problem, see issue #4087"),
9384 child_relpath, repos_root_url, last_repos_root_url);
9385 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
9387 child->repos_root_url = repos_root_url;
9388 child->repos_uuid = repos_uuid;
9391 child->changed_rev = svn_sqlite__column_revnum(stmt, 8);
9393 child->changed_date = svn_sqlite__column_int64(stmt, 9);
9395 child->changed_author = svn_sqlite__column_text(stmt, 10,
9398 if (child->kind != svn_node_dir)
9399 child->depth = svn_depth_unknown;
9402 child->has_descendants = TRUE;
9403 child_item->was_dir = TRUE;
9404 child->depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
9408 err = is_wclocked(&child->locked, wcroot, child_relpath,
9412 SVN_ERR(svn_error_compose_create(err,
9413 svn_sqlite__reset(stmt)));
9417 child->recorded_time = svn_sqlite__column_int64(stmt, 13);
9418 child->recorded_size = get_recorded_size(stmt, 7);
9419 child->has_checksum = !svn_sqlite__column_is_null(stmt, 6);
9420 child->copied = op_depth > 0 && !svn_sqlite__column_is_null(stmt, 2);
9421 child->had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
9423 if (child->had_props)
9425 apr_hash_t *properties;
9426 err = svn_sqlite__column_properties(&properties, stmt, 14,
9427 scratch_pool, scratch_pool);
9429 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9431 child->special = (child->had_props
9432 && svn_hash_gets(properties, SVN_PROP_SPECIAL));
9436 child->op_root = FALSE;
9438 child->op_root = (op_depth == relpath_depth(child_relpath));
9440 if (op_depth && child->op_root)
9441 child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
9444 svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
9446 else if (!child_item->was_dir
9447 && svn_sqlite__column_token(stmt, 4, kind_map) == svn_node_dir)
9449 child_item->was_dir = TRUE;
9451 err = find_conflict_descendants(&child_item->info.has_descendants,
9452 wcroot, child_relpath,
9455 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9460 child_item->info.have_base = TRUE;
9462 /* Get the lock info, available only at op_depth 0. */
9463 child_item->info.lock = lock_from_columns(stmt, 15, 16, 17, 18,
9466 /* FILE_EXTERNAL flag only on op_depth 0. */
9467 child_item->info.file_external = svn_sqlite__column_boolean(stmt,
9472 const char *moved_to_relpath;
9474 child_item->nr_layers++;
9475 child_item->info.have_more_work = (child_item->nr_layers > 1);
9478 /* A local_relpath can be moved multiple times at different op
9479 depths and it really depends on the caller what is interesting.
9480 We provide a simple linked list with the moved_from information */
9482 moved_to_relpath = svn_sqlite__column_text(stmt, 21, NULL);
9483 if (moved_to_relpath)
9485 struct svn_wc__db_moved_to_info_t *moved_to;
9486 struct svn_wc__db_moved_to_info_t **next;
9487 const char *shadow_op_relpath;
9489 moved_to = apr_pcalloc(result_pool, sizeof(*moved_to));
9490 moved_to->moved_to_abspath = svn_dirent_join(wcroot->abspath,
9494 shadow_op_relpath = svn_relpath_prefix(child_relpath, op_depth,
9497 moved_to->shadow_op_root_abspath =
9498 svn_dirent_join(wcroot->abspath, shadow_op_relpath,
9501 next = &child_item->info.moved_to;
9504 0 < strcmp((*next)->shadow_op_root_abspath,
9505 moved_to->shadow_op_root_abspath))
9506 next = &((*next)->next);
9508 moved_to->next = *next;
9513 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9516 SVN_ERR(svn_sqlite__reset(stmt));
9518 if (!base_tree_only)
9520 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9521 STMT_SELECT_ACTUAL_CHILDREN_INFO));
9522 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9523 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9527 struct read_children_info_item_t *child_item;
9528 struct svn_wc__db_info_t *child;
9529 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9530 const char *name = svn_relpath_basename(child_relpath, NULL);
9532 child_item = svn_hash_gets(nodes, name);
9535 child_item = apr_pcalloc(result_pool, sizeof(*child_item));
9536 child_item->info.status = svn_wc__db_status_not_present;
9539 child = &child_item->info;
9541 child->changelist = svn_sqlite__column_text(stmt, 1, result_pool);
9543 child->props_mod = !svn_sqlite__column_is_null(stmt, 2);
9545 if (child->props_mod)
9548 apr_hash_t *properties;
9550 err = svn_sqlite__column_properties(&properties, stmt, 2,
9551 scratch_pool, scratch_pool);
9553 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9554 child->special = (NULL != svn_hash_gets(properties,
9560 child->conflicted = !svn_sqlite__column_is_null(stmt, 3);
9562 if (child->conflicted)
9563 svn_hash_sets(conflicts, apr_pstrdup(result_pool, name), "");
9565 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9568 SVN_ERR(svn_sqlite__reset(stmt));
9571 return SVN_NO_ERROR;
9575 svn_wc__db_read_children_info(apr_hash_t **nodes,
9576 apr_hash_t **conflicts,
9578 const char *dir_abspath,
9579 svn_boolean_t base_tree_only,
9580 apr_pool_t *result_pool,
9581 apr_pool_t *scratch_pool)
9583 svn_wc__db_wcroot_t *wcroot;
9584 const char *dir_relpath;
9586 *conflicts = apr_hash_make(result_pool);
9587 *nodes = apr_hash_make(result_pool);
9588 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9590 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9592 scratch_pool, scratch_pool));
9593 VERIFY_USABLE_WCROOT(wcroot);
9595 SVN_WC__DB_WITH_TXN(
9596 read_children_info(wcroot, dir_relpath, *conflicts, *nodes,
9597 base_tree_only, result_pool, scratch_pool),
9600 return SVN_NO_ERROR;
9603 /* Implementation of svn_wc__db_read_single_info.
9605 ### This function is very similar to a lot of code inside
9606 read_children_info, but that performs some tricks to re-use data between
9609 ### We read the same few NODES records a few times via different helper
9610 functions, so this could be optimized bit, but everything is within
9611 a sqlite transaction and all queries are backed by an index, so generally
9612 everything (including the used indexes) should be in the sqlite page cache
9613 after the first query.
9615 static svn_error_t *
9616 read_single_info(const struct svn_wc__db_info_t **info,
9617 svn_wc__db_wcroot_t *wcroot,
9618 const char *local_relpath,
9619 svn_boolean_t base_tree_only,
9620 apr_pool_t *result_pool,
9621 apr_pool_t *scratch_pool)
9623 struct svn_wc__db_info_t *mtb;
9624 apr_int64_t repos_id;
9625 const svn_checksum_t *checksum;
9626 const char *original_repos_relpath;
9627 svn_boolean_t have_work;
9628 apr_hash_t *properties;
9630 mtb = apr_pcalloc(result_pool, sizeof(*mtb));
9632 if (!base_tree_only)
9633 SVN_ERR(read_info(&mtb->status, &mtb->kind, &mtb->revnum,
9634 &mtb->repos_relpath, &repos_id, &mtb->changed_rev,
9635 &mtb->changed_date, &mtb->changed_author, &mtb->depth,
9636 &checksum, NULL, &original_repos_relpath, NULL, NULL,
9637 &mtb->lock, &mtb->recorded_size, &mtb->recorded_time,
9638 &mtb->changelist, &mtb->conflicted, &mtb->op_root,
9639 &mtb->had_props, &mtb->props_mod, &mtb->have_base,
9640 &mtb->have_more_work, &have_work,
9641 wcroot, local_relpath, result_pool, scratch_pool));
9644 svn_boolean_t update_root;
9647 original_repos_relpath = NULL;
9649 SVN_ERR(svn_wc__db_base_get_info_internal(
9650 &mtb->status, &mtb->kind, &mtb->revnum, &mtb->repos_relpath,
9651 &repos_id, &mtb->changed_rev, &mtb->changed_date,
9652 &mtb->changed_author, &mtb->depth, &checksum, NULL,
9653 &mtb->lock, &mtb->had_props, &properties, &update_root,
9654 wcroot, local_relpath, scratch_pool, scratch_pool));
9656 mtb->have_base = TRUE;
9657 mtb->file_external = (update_root && mtb->kind == svn_node_file);
9660 /* Query the same rows in the database again for move information */
9661 if (have_work && (mtb->have_base || mtb->have_more_work))
9663 svn_sqlite__stmt_t *stmt;
9664 svn_boolean_t have_row;
9666 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9667 STMT_SELECT_MOVED_TO_NODE));
9668 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9670 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9674 struct svn_wc__db_moved_to_info_t *move;
9675 int op_depth = svn_sqlite__column_int(stmt, 0);
9676 const char *moved_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
9677 const char *cur_relpath;
9679 move = apr_pcalloc(result_pool, sizeof(*move));
9680 move->moved_to_abspath = svn_dirent_join(wcroot->abspath,
9684 cur_relpath = svn_relpath_prefix(local_relpath, op_depth,
9687 move->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath,
9691 move->next = mtb->moved_to;
9692 mtb->moved_to = move;
9694 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9697 SVN_ERR(svn_sqlite__reset(stmt));
9700 /* Maybe we have to get some shadowed lock from BASE to make our test suite
9701 happy... (It might be completely unrelated, but...)
9702 This queries the same BASE row again, joined to the lock table */
9703 if (!base_tree_only && mtb->have_base
9704 && (have_work || mtb->kind == svn_node_file))
9706 svn_boolean_t update_root;
9707 svn_wc__db_lock_t **lock_arg = NULL;
9710 lock_arg = &mtb->lock;
9712 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL, NULL,
9713 NULL, NULL, NULL, NULL, NULL,
9714 NULL, lock_arg, NULL, NULL,
9716 wcroot, local_relpath,
9717 result_pool, scratch_pool));
9719 mtb->file_external = (update_root && mtb->kind == svn_node_file);
9722 if (mtb->status == svn_wc__db_status_added)
9724 svn_wc__db_status_t status;
9726 SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9728 wcroot, local_relpath,
9729 result_pool, scratch_pool));
9731 mtb->moved_here = (status == svn_wc__db_status_moved_here);
9732 mtb->incomplete = (status == svn_wc__db_status_incomplete);
9736 if (mtb->kind == svn_node_file
9737 && (mtb->had_props || mtb->props_mod
9738 || (base_tree_only && properties)))
9740 if (!base_tree_only)
9743 SVN_ERR(svn_wc__db_read_props_internal(&properties,
9744 wcroot, local_relpath,
9745 scratch_pool, scratch_pool));
9747 SVN_ERR(db_read_pristine_props(&properties, wcroot, local_relpath,
9748 TRUE /* deleted_ok */,
9749 scratch_pool, scratch_pool));
9752 mtb->special = (NULL != svn_hash_gets(properties, SVN_PROP_SPECIAL));
9756 mtb->has_checksum = (checksum != NULL);
9757 mtb->copied = (original_repos_relpath != NULL);
9759 SVN_ERR(svn_wc__db_fetch_repos_info(&mtb->repos_root_url, &mtb->repos_uuid,
9760 wcroot, repos_id, result_pool));
9762 if (!base_tree_only && mtb->kind == svn_node_dir)
9763 SVN_ERR(is_wclocked(&mtb->locked, wcroot, local_relpath, scratch_pool));
9765 if (mtb->kind == svn_node_dir)
9766 mtb->has_descendants = TRUE;
9768 SVN_ERR(find_conflict_descendants(&mtb->has_descendants,
9769 wcroot, local_relpath, scratch_pool));
9773 return SVN_NO_ERROR;
9777 svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info,
9779 const char *local_abspath,
9780 svn_boolean_t base_tree_only,
9781 apr_pool_t *result_pool,
9782 apr_pool_t *scratch_pool)
9784 svn_wc__db_wcroot_t *wcroot;
9785 const char *local_relpath;
9787 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9789 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9791 scratch_pool, scratch_pool));
9792 VERIFY_USABLE_WCROOT(wcroot);
9794 SVN_WC__DB_WITH_TXN(read_single_info(info, wcroot, local_relpath,
9796 result_pool, scratch_pool),
9799 return SVN_NO_ERROR;
9803 svn_wc__db_read_pristine_info(svn_wc__db_status_t *status,
9804 svn_node_kind_t *kind,
9805 svn_revnum_t *changed_rev,
9806 apr_time_t *changed_date,
9807 const char **changed_author,
9808 svn_depth_t *depth, /* dirs only */
9809 const svn_checksum_t **checksum, /* files only */
9810 const char **target, /* symlinks only */
9811 svn_boolean_t *had_props,
9814 const char *local_abspath,
9815 apr_pool_t *result_pool,
9816 apr_pool_t *scratch_pool)
9818 svn_wc__db_wcroot_t *wcroot;
9819 const char *local_relpath;
9820 svn_sqlite__stmt_t *stmt;
9821 svn_boolean_t have_row;
9822 svn_error_t *err = NULL;
9824 svn_wc__db_status_t raw_status;
9825 svn_node_kind_t node_kind;
9827 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9829 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9831 scratch_pool, scratch_pool));
9832 VERIFY_USABLE_WCROOT(wcroot);
9834 /* Obtain the most likely to exist record first, to make sure we don't
9835 have to obtain the SQLite read-lock multiple times */
9836 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9837 STMT_SELECT_NODE_INFO));
9838 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9839 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9843 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9844 svn_sqlite__reset(stmt),
9845 _("The node '%s' was not found."),
9846 path_for_error_message(wcroot,
9851 op_depth = svn_sqlite__column_int(stmt, 0);
9852 raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9854 if (op_depth > 0 && raw_status == svn_wc__db_status_base_deleted)
9856 SVN_ERR(svn_sqlite__step_row(stmt));
9858 op_depth = svn_sqlite__column_int(stmt, 0);
9859 raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9862 node_kind = svn_sqlite__column_token(stmt, 4, kind_map);
9868 err = svn_error_compose_create(err,
9869 convert_to_working_status(
9874 *status = raw_status;
9882 *changed_rev = svn_sqlite__column_revnum(stmt, 8);
9886 *changed_date = svn_sqlite__column_int64(stmt, 9);
9890 *changed_author = svn_sqlite__column_text(stmt, 10,
9895 if (node_kind != svn_node_dir)
9897 *depth = svn_depth_unknown;
9901 *depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
9907 if (node_kind != svn_node_file)
9914 err2 = svn_sqlite__column_checksum(checksum, stmt, 6, result_pool);
9919 err = svn_error_compose_create(
9923 _("The node '%s' has a corrupt checksum value."),
9924 path_for_error_message(wcroot, local_relpath,
9933 if (node_kind != svn_node_symlink)
9936 *target = svn_sqlite__column_text(stmt, 12, result_pool);
9940 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
9944 if (raw_status == svn_wc__db_status_normal
9945 || raw_status == svn_wc__db_status_incomplete)
9947 SVN_ERR(svn_sqlite__column_properties(props, stmt, 14,
9948 result_pool, scratch_pool));
9950 *props = apr_hash_make(result_pool);
9954 assert(svn_sqlite__column_is_null(stmt, 14));
9959 return svn_error_trace(
9960 svn_error_compose_create(err,
9961 svn_sqlite__reset(stmt)));
9965 svn_wc__db_read_children_walker_info(const apr_array_header_t **items,
9967 const char *dir_abspath,
9968 apr_pool_t *result_pool,
9969 apr_pool_t *scratch_pool)
9971 svn_wc__db_wcroot_t *wcroot;
9972 const char *dir_relpath;
9973 svn_sqlite__stmt_t *stmt;
9974 svn_boolean_t have_row;
9975 apr_array_header_t *nodes;
9977 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9979 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9981 scratch_pool, scratch_pool));
9982 VERIFY_USABLE_WCROOT(wcroot);
9984 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9985 STMT_SELECT_NODE_CHILDREN_WALKER_INFO));
9986 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9987 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9989 nodes = apr_array_make(result_pool, 16,
9990 sizeof(struct svn_wc__db_walker_info_t *));
9993 struct svn_wc__db_walker_info_t *child;
9994 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9995 const char *name = svn_relpath_basename(child_relpath, result_pool);
9996 int op_depth = svn_sqlite__column_int(stmt, 1);
9999 child = apr_palloc(result_pool, sizeof(*child));
10000 child->name = name;
10001 child->status = svn_sqlite__column_token(stmt, 2, presence_map);
10004 err = convert_to_working_status(&child->status, child->status);
10006 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10008 child->kind = svn_sqlite__column_token(stmt, 3, kind_map);
10010 APR_ARRAY_PUSH(nodes, struct svn_wc__db_walker_info_t *) = child;
10012 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10015 SVN_ERR(svn_sqlite__reset(stmt));
10019 return SVN_NO_ERROR;
10023 svn_wc__db_read_node_install_info(const char **wcroot_abspath,
10024 const svn_checksum_t **sha1_checksum,
10025 apr_hash_t **pristine_props,
10026 apr_time_t *changed_date,
10028 const char *local_abspath,
10029 const char *wri_abspath,
10030 apr_pool_t *result_pool,
10031 apr_pool_t *scratch_pool)
10033 svn_wc__db_wcroot_t *wcroot;
10034 const char *local_relpath;
10035 svn_sqlite__stmt_t *stmt;
10036 svn_error_t *err = NULL;
10037 svn_boolean_t have_row;
10039 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10042 wri_abspath = local_abspath;
10044 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10045 wri_abspath, scratch_pool, scratch_pool));
10046 VERIFY_USABLE_WCROOT(wcroot);
10048 if (local_abspath != wri_abspath
10049 && strcmp(local_abspath, wri_abspath))
10051 if (!svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
10052 return svn_error_createf(
10053 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
10054 _("The node '%s' is not in working copy '%s'"),
10055 svn_dirent_local_style(local_abspath, scratch_pool),
10056 svn_dirent_local_style(wcroot->abspath, scratch_pool));
10058 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
10061 if (wcroot_abspath != NULL)
10062 *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
10064 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10065 STMT_SELECT_NODE_INFO));
10067 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10069 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10074 err = svn_sqlite__column_checksum(sha1_checksum, stmt, 6, result_pool);
10076 if (!err && pristine_props)
10078 err = svn_sqlite__column_properties(pristine_props, stmt, 14,
10079 result_pool, scratch_pool);
10080 /* Null means no props (assuming presence normal or incomplete). */
10081 if (*pristine_props == NULL)
10082 *pristine_props = apr_hash_make(result_pool);
10086 *changed_date = svn_sqlite__column_int64(stmt, 9);
10089 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10090 svn_sqlite__reset(stmt),
10091 _("The node '%s' is not installable"),
10092 svn_dirent_local_style(local_abspath,
10095 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10097 return SVN_NO_ERROR;
10102 /* The body of svn_wc__db_read_repos_info().
10104 static svn_error_t *
10105 db_read_repos_info(svn_revnum_t *revision,
10106 const char **repos_relpath,
10107 apr_int64_t *repos_id,
10108 svn_wc__db_wcroot_t *wcroot,
10109 const char *local_relpath,
10110 apr_pool_t *result_pool,
10111 apr_pool_t *scratch_pool)
10113 svn_wc__db_status_t status;
10115 SVN_ERR(read_info(&status, NULL, revision, repos_relpath, repos_id, NULL,
10116 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10117 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10119 wcroot, local_relpath, result_pool, scratch_pool));
10121 if ((repos_relpath && !*repos_relpath)
10122 || (repos_id && *repos_id == INVALID_REPOS_ID))
10124 if (status == svn_wc__db_status_added)
10126 SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL,
10127 NULL, NULL, NULL, NULL, NULL,
10128 wcroot, local_relpath,
10129 result_pool, scratch_pool));
10131 else if (status == svn_wc__db_status_deleted)
10133 const char *base_del_relpath;
10134 const char *work_del_relpath;
10136 SVN_ERR(scan_deletion(&base_del_relpath, NULL,
10143 if (work_del_relpath)
10145 /* The parent of the WORKING delete, must be an addition */
10146 const char *work_relpath = NULL;
10148 /* work_del_relpath should not be NULL. However, we have
10149 * observed instances where that assumption was not met.
10150 * Bail out in that case instead of crashing with a segfault.
10152 SVN_ERR_ASSERT(work_del_relpath != NULL);
10153 work_relpath = svn_relpath_dirname(work_del_relpath,
10156 SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id,
10157 NULL, NULL, NULL, NULL, NULL, NULL,
10158 wcroot, work_relpath,
10159 scratch_pool, scratch_pool));
10162 *repos_relpath = svn_relpath_join(
10164 svn_dirent_skip_ancestor(work_relpath,
10168 else if (base_del_relpath)
10170 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, revision,
10175 NULL, NULL, NULL, NULL,
10182 *repos_relpath = svn_relpath_join(
10184 svn_dirent_skip_ancestor(base_del_relpath,
10189 SVN_ERR_MALFUNCTION();
10191 else if (status == svn_wc__db_status_excluded)
10193 const char *parent_relpath;
10196 /* A BASE excluded would have had repository information, so
10197 we have a working exclude, which must be below an addition */
10199 svn_relpath_split(&parent_relpath, &name, local_relpath,
10201 SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL,
10202 NULL, NULL, NULL, NULL, NULL,
10203 wcroot, parent_relpath,
10204 scratch_pool, scratch_pool));
10207 *repos_relpath = svn_relpath_join(*repos_relpath, name,
10210 return SVN_NO_ERROR;
10214 /* All working statee are explicitly handled and all base statee
10215 have a repos_relpath */
10216 SVN_ERR_MALFUNCTION();
10220 return SVN_NO_ERROR;
10225 svn_wc__db_read_repos_info(svn_revnum_t *revision,
10226 const char **repos_relpath,
10227 const char **repos_root_url,
10228 const char **repos_uuid,
10230 const char *local_abspath,
10231 apr_pool_t *result_pool,
10232 apr_pool_t *scratch_pool)
10234 svn_wc__db_wcroot_t *wcroot;
10235 const char *local_relpath;
10236 apr_int64_t repos_id = INVALID_REPOS_ID;
10238 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10240 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10242 scratch_pool, scratch_pool));
10243 VERIFY_USABLE_WCROOT(wcroot);
10245 SVN_WC__DB_WITH_TXN4(db_read_repos_info(revision, repos_relpath,
10246 (repos_root_url || repos_uuid)
10247 ? &repos_id : NULL,
10248 wcroot, local_relpath,
10249 result_pool, scratch_pool),
10250 svn_wc__db_fetch_repos_info(repos_root_url,
10254 SVN_NO_ERROR, SVN_NO_ERROR,
10257 return SVN_NO_ERROR;
10261 /* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
10262 a hash table mapping <tt>char *</tt> names onto svn_string_t *
10263 values for any properties of immediate or recursive child nodes of
10264 LOCAL_ABSPATH, the actual query being determined by STMT_IDX.
10265 If FILES_ONLY is true, only report properties for file child nodes.
10266 Check for cancellation between calls of RECEIVER_FUNC.
10268 typedef struct cache_props_baton_t
10271 svn_boolean_t pristine;
10272 const apr_array_header_t *changelists;
10273 svn_cancel_func_t cancel_func;
10274 void *cancel_baton;
10275 } cache_props_baton_t;
10278 static svn_error_t *
10279 cache_props_recursive(void *cb_baton,
10280 svn_wc__db_wcroot_t *wcroot,
10281 const char *local_relpath,
10282 apr_pool_t *scratch_pool)
10284 cache_props_baton_t *baton = cb_baton;
10285 svn_sqlite__stmt_t *stmt;
10288 SVN_ERR(populate_targets_tree(wcroot, local_relpath, baton->depth,
10289 baton->changelists, scratch_pool));
10291 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
10292 STMT_CREATE_TARGET_PROP_CACHE));
10294 if (baton->pristine)
10295 stmt_idx = STMT_CACHE_TARGET_PRISTINE_PROPS;
10297 stmt_idx = STMT_CACHE_TARGET_PROPS;
10299 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
10300 SVN_ERR(svn_sqlite__bind_int64(stmt, 1, wcroot->wc_id));
10301 SVN_ERR(svn_sqlite__step_done(stmt));
10303 return SVN_NO_ERROR;
10308 svn_wc__db_read_props_streamily(svn_wc__db_t *db,
10309 const char *local_abspath,
10311 svn_boolean_t pristine,
10312 const apr_array_header_t *changelists,
10313 svn_wc__proplist_receiver_t receiver_func,
10314 void *receiver_baton,
10315 svn_cancel_func_t cancel_func,
10316 void *cancel_baton,
10317 apr_pool_t *scratch_pool)
10319 svn_wc__db_wcroot_t *wcroot;
10320 const char *local_relpath;
10321 svn_sqlite__stmt_t *stmt;
10322 cache_props_baton_t baton;
10323 svn_boolean_t have_row;
10324 apr_pool_t *iterpool;
10325 svn_error_t *err = NULL;
10327 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10328 SVN_ERR_ASSERT(receiver_func);
10329 SVN_ERR_ASSERT((depth == svn_depth_files) ||
10330 (depth == svn_depth_immediates) ||
10331 (depth == svn_depth_infinity));
10333 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10335 scratch_pool, scratch_pool));
10336 VERIFY_USABLE_WCROOT(wcroot);
10338 baton.depth = depth;
10339 baton.pristine = pristine;
10340 baton.changelists = changelists;
10341 baton.cancel_func = cancel_func;
10342 baton.cancel_baton = cancel_baton;
10344 SVN_ERR(with_finalization(wcroot, local_relpath,
10345 cache_props_recursive, &baton,
10347 cancel_func, cancel_baton,
10349 STMT_DROP_TARGETS_LIST,
10352 iterpool = svn_pool_create(scratch_pool);
10354 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10355 STMT_SELECT_ALL_TARGET_PROP_CACHE));
10356 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10357 while (!err && have_row)
10361 svn_pool_clear(iterpool);
10363 SVN_ERR(svn_sqlite__column_properties(&props, stmt, 1, iterpool,
10366 /* see if someone wants to cancel this operation. */
10368 err = cancel_func(cancel_baton);
10370 if (!err && props && apr_hash_count(props) != 0)
10372 const char *child_relpath;
10373 const char *child_abspath;
10375 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
10376 child_abspath = svn_dirent_join(wcroot->abspath,
10377 child_relpath, iterpool);
10379 err = receiver_func(receiver_baton, child_abspath, props, iterpool);
10382 err = svn_error_compose_create(err, svn_sqlite__step(&have_row, stmt));
10385 err = svn_error_compose_create(err, svn_sqlite__reset(stmt));
10387 svn_pool_destroy(iterpool);
10389 SVN_ERR(svn_error_compose_create(
10391 svn_sqlite__exec_statements(wcroot->sdb,
10392 STMT_DROP_TARGET_PROP_CACHE)));
10393 return SVN_NO_ERROR;
10397 /* Helper for svn_wc__db_read_props().
10400 svn_wc__db_read_props_internal(apr_hash_t **props,
10401 svn_wc__db_wcroot_t *wcroot,
10402 const char *local_relpath,
10403 apr_pool_t *result_pool,
10404 apr_pool_t *scratch_pool)
10406 svn_sqlite__stmt_t *stmt;
10407 svn_boolean_t have_row;
10408 svn_error_t *err = NULL;
10410 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10411 STMT_SELECT_ACTUAL_PROPS));
10412 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10413 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10415 if (have_row && !svn_sqlite__column_is_null(stmt, 0))
10417 err = svn_sqlite__column_properties(props, stmt, 0,
10418 result_pool, scratch_pool);
10423 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10426 return SVN_NO_ERROR;
10428 /* No local changes. Return the pristine props for this node. */
10429 SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, FALSE,
10430 result_pool, scratch_pool));
10431 if (*props == NULL)
10433 /* Pristine properties are not defined for this node.
10434 ### we need to determine whether this node is in a state that
10435 ### allows for ACTUAL properties (ie. not deleted). for now,
10436 ### just say all nodes, no matter the state, have at least an
10437 ### empty set of props. */
10438 *props = apr_hash_make(result_pool);
10441 return SVN_NO_ERROR;
10446 svn_wc__db_read_props(apr_hash_t **props,
10448 const char *local_abspath,
10449 apr_pool_t *result_pool,
10450 apr_pool_t *scratch_pool)
10452 svn_wc__db_wcroot_t *wcroot;
10453 const char *local_relpath;
10455 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10457 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10458 local_abspath, scratch_pool, scratch_pool));
10459 VERIFY_USABLE_WCROOT(wcroot);
10461 SVN_WC__DB_WITH_TXN(svn_wc__db_read_props_internal(props, wcroot,
10467 return SVN_NO_ERROR;
10471 static svn_error_t *
10472 db_read_pristine_props(apr_hash_t **props,
10473 svn_wc__db_wcroot_t *wcroot,
10474 const char *local_relpath,
10475 svn_boolean_t deleted_ok,
10476 apr_pool_t *result_pool,
10477 apr_pool_t *scratch_pool)
10479 svn_sqlite__stmt_t *stmt;
10480 svn_boolean_t have_row;
10481 svn_wc__db_status_t presence;
10483 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_NODE_PROPS));
10484 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10486 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10490 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10491 svn_sqlite__reset(stmt),
10492 _("The node '%s' was not found."),
10493 path_for_error_message(wcroot,
10499 /* Examine the presence: */
10500 presence = svn_sqlite__column_token(stmt, 1, presence_map);
10502 /* For "base-deleted", it is obvious the pristine props are located
10503 below the current node. Fetch the NODE from the next record. */
10504 if (presence == svn_wc__db_status_base_deleted && deleted_ok)
10506 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10508 SVN_ERR_ASSERT(have_row);
10510 presence = svn_sqlite__column_token(stmt, 1, presence_map);
10513 /* normal or copied: Fetch properties (during update we want
10514 properties for incomplete as well) */
10515 if (presence == svn_wc__db_status_normal
10516 || presence == svn_wc__db_status_incomplete)
10520 err = svn_sqlite__column_properties(props, stmt, 0, result_pool,
10522 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10525 *props = apr_hash_make(result_pool);
10527 return SVN_NO_ERROR;
10530 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
10531 svn_sqlite__reset(stmt),
10532 _("The node '%s' has a status that"
10533 " has no properties."),
10534 path_for_error_message(wcroot,
10541 svn_wc__db_read_pristine_props(apr_hash_t **props,
10543 const char *local_abspath,
10544 apr_pool_t *result_pool,
10545 apr_pool_t *scratch_pool)
10547 svn_wc__db_wcroot_t *wcroot;
10548 const char *local_relpath;
10550 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10552 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10553 local_abspath, scratch_pool, scratch_pool));
10554 VERIFY_USABLE_WCROOT(wcroot);
10556 SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, TRUE,
10557 result_pool, scratch_pool));
10558 return SVN_NO_ERROR;
10562 svn_wc__db_prop_retrieve_recursive(apr_hash_t **values,
10564 const char *local_abspath,
10565 const char *propname,
10566 apr_pool_t *result_pool,
10567 apr_pool_t *scratch_pool)
10569 svn_wc__db_wcroot_t *wcroot;
10570 const char *local_relpath;
10571 svn_sqlite__stmt_t *stmt;
10572 svn_boolean_t have_row;
10573 apr_pool_t *iterpool;
10575 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10577 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10578 local_abspath, scratch_pool, scratch_pool));
10579 VERIFY_USABLE_WCROOT(wcroot);
10581 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10582 STMT_SELECT_CURRENT_PROPS_RECURSIVE));
10583 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10585 *values = apr_hash_make(result_pool);
10587 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10588 iterpool = svn_pool_create(scratch_pool);
10591 apr_hash_t *node_props;
10592 svn_string_t *value;
10594 svn_pool_clear(iterpool);
10596 SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 0,
10597 iterpool, iterpool));
10599 value = (node_props
10600 ? svn_hash_gets(node_props, propname)
10605 svn_hash_sets(*values,
10606 svn_dirent_join(wcroot->abspath,
10607 svn_sqlite__column_text(stmt, 1, NULL),
10609 svn_string_dup(value, result_pool));
10612 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10615 svn_pool_destroy(iterpool);
10617 return svn_error_trace(svn_sqlite__reset(stmt));
10620 /* Remove all prop name value pairs from PROP_HASH where the property
10621 name is not PROPNAME. */
10623 filter_unwanted_props(apr_hash_t *prop_hash,
10624 const char * propname,
10625 apr_pool_t *scratch_pool)
10627 apr_hash_index_t *hi;
10629 for (hi = apr_hash_first(scratch_pool, prop_hash);
10631 hi = apr_hash_next(hi))
10633 const char *ipropname = apr_hash_this_key(hi);
10635 if (strcmp(ipropname, propname) != 0)
10636 svn_hash_sets(prop_hash, ipropname, NULL);
10641 /* Get the changed properties as stored in the ACTUAL table */
10642 static svn_error_t *
10643 db_get_changed_props(apr_hash_t **actual_props,
10644 svn_wc__db_wcroot_t *wcroot,
10645 const char *local_relpath,
10646 apr_pool_t *result_pool,
10647 apr_pool_t *scratch_pool)
10649 svn_sqlite__stmt_t *stmt;
10650 svn_boolean_t have_row;
10651 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10652 STMT_SELECT_ACTUAL_PROPS));
10653 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10654 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10656 if (have_row && !svn_sqlite__column_is_null(stmt, 0))
10657 SVN_ERR(svn_sqlite__column_properties(actual_props, stmt, 0,
10658 result_pool, scratch_pool));
10660 *actual_props = NULL; /* Cached when we read that record */
10662 return svn_error_trace(svn_sqlite__reset(stmt));
10665 /* The body of svn_wc__db_read_inherited_props(). */
10666 static svn_error_t *
10667 db_read_inherited_props(apr_array_header_t **inherited_props,
10668 apr_hash_t **actual_props,
10669 svn_wc__db_wcroot_t *wcroot,
10670 const char *local_relpath,
10671 const char *propname,
10672 apr_pool_t *result_pool,
10673 apr_pool_t *scratch_pool)
10676 apr_array_header_t *cached_iprops = NULL;
10677 apr_array_header_t *iprops;
10678 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
10679 svn_sqlite__stmt_t *stmt;
10680 const char *relpath;
10681 const char *expected_parent_repos_relpath = NULL;
10682 const char *parent_relpath;
10684 iprops = apr_array_make(result_pool, 1,
10685 sizeof(svn_prop_inherited_item_t *));
10686 *inherited_props = iprops;
10689 *actual_props = NULL;
10691 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10692 STMT_SELECT_NODE_INFO));
10694 relpath = local_relpath;
10696 /* Walk up to the root of the WC looking for inherited properties. When we
10697 reach the WC root also check for cached inherited properties. */
10698 for (relpath = local_relpath; relpath; relpath = parent_relpath)
10700 svn_boolean_t have_row;
10702 svn_wc__db_status_t status;
10703 apr_hash_t *node_props;
10705 parent_relpath = relpath[0] ? svn_relpath_dirname(relpath, scratch_pool)
10708 svn_pool_clear(iterpool);
10710 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, relpath));
10712 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10715 return svn_error_createf(
10716 SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
10717 _("The node '%s' was not found."),
10718 path_for_error_message(wcroot, relpath,
10721 op_depth = svn_sqlite__column_int(stmt, 0);
10723 status = svn_sqlite__column_token(stmt, 3, presence_map);
10725 if (status != svn_wc__db_status_normal
10726 && status != svn_wc__db_status_incomplete)
10727 return svn_error_createf(
10728 SVN_ERR_WC_PATH_UNEXPECTED_STATUS, svn_sqlite__reset(stmt),
10729 _("The node '%s' has a status that has no properties."),
10730 path_for_error_message(wcroot, relpath,
10735 /* WORKING node. Nothing to check */
10737 else if (expected_parent_repos_relpath)
10739 const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10741 if (strcmp(expected_parent_repos_relpath, repos_relpath) != 0)
10743 /* The child of this node has a different parent than this node
10744 (It is "switched"), so we can stop here. Note that switched
10745 with the same parent is not interesting for us here. */
10746 SVN_ERR(svn_sqlite__reset(stmt));
10750 expected_parent_repos_relpath =
10751 svn_relpath_dirname(expected_parent_repos_relpath, scratch_pool);
10755 const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10757 expected_parent_repos_relpath =
10758 svn_relpath_dirname(repos_relpath, scratch_pool);
10762 && !svn_sqlite__column_is_null(stmt, 16))
10764 /* The node contains a cache. No reason to look further */
10765 SVN_ERR(svn_sqlite__column_iprops(&cached_iprops, stmt, 16,
10766 result_pool, iterpool));
10768 parent_relpath = NULL; /* Stop after this */
10771 SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 14,
10772 iterpool, iterpool));
10774 SVN_ERR(svn_sqlite__reset(stmt));
10776 /* If PARENT_ABSPATH is a parent of LOCAL_ABSPATH, then LOCAL_ABSPATH
10777 can inherit properties from it. */
10778 if (relpath != local_relpath)
10780 apr_hash_t *changed_props;
10782 SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10783 result_pool, iterpool));
10786 node_props = changed_props;
10787 else if (node_props)
10788 node_props = svn_prop_hash_dup(node_props, result_pool);
10790 if (node_props && apr_hash_count(node_props))
10792 /* If we only want PROPNAME filter out any other properties. */
10794 filter_unwanted_props(node_props, propname, iterpool);
10796 if (apr_hash_count(node_props))
10798 svn_prop_inherited_item_t *iprop_elt =
10799 apr_pcalloc(result_pool,
10800 sizeof(svn_prop_inherited_item_t));
10801 iprop_elt->path_or_url = svn_dirent_join(wcroot->abspath,
10805 iprop_elt->prop_hash = node_props;
10806 /* Build the output array in depth-first order. */
10807 svn_sort__array_insert(iprops, &iprop_elt, 0);
10811 else if (actual_props)
10813 apr_hash_t *changed_props;
10815 SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10816 result_pool, iterpool));
10819 *actual_props = changed_props;
10820 else if (node_props)
10821 *actual_props = svn_prop_hash_dup(node_props, result_pool);
10827 for (i = cached_iprops->nelts - 1; i >= 0; i--)
10829 svn_prop_inherited_item_t *cached_iprop =
10830 APR_ARRAY_IDX(cached_iprops, i, svn_prop_inherited_item_t *);
10832 /* An empty property hash in the iprops cache means there are no
10833 inherited properties. */
10834 if (apr_hash_count(cached_iprop->prop_hash) == 0)
10838 filter_unwanted_props(cached_iprop->prop_hash, propname,
10841 /* If we didn't filter everything then keep this iprop. */
10842 if (apr_hash_count(cached_iprop->prop_hash))
10843 svn_sort__array_insert(iprops, &cached_iprop, 0);
10847 if (actual_props && !*actual_props)
10848 *actual_props = apr_hash_make(result_pool);
10850 svn_pool_destroy(iterpool);
10851 return SVN_NO_ERROR;
10855 svn_wc__db_read_inherited_props(apr_array_header_t **iprops,
10856 apr_hash_t **actual_props,
10858 const char *local_abspath,
10859 const char *propname,
10860 apr_pool_t *result_pool,
10861 apr_pool_t *scratch_pool)
10863 svn_wc__db_wcroot_t *wcroot;
10864 const char *local_relpath;
10866 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10868 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10870 scratch_pool, scratch_pool));
10871 VERIFY_USABLE_WCROOT(wcroot);
10873 SVN_WC__DB_WITH_TXN(db_read_inherited_props(iprops, actual_props,
10874 wcroot, local_relpath, propname,
10875 result_pool, scratch_pool),
10878 return SVN_NO_ERROR;
10881 /* The body of svn_wc__db_get_children_with_cached_iprops().
10883 static svn_error_t *
10884 get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10885 svn_wc__db_wcroot_t *wcroot,
10886 const char *local_relpath,
10888 apr_pool_t *result_pool,
10889 apr_pool_t *scratch_pool)
10891 svn_sqlite__stmt_t *stmt;
10892 svn_boolean_t have_row;
10894 *iprop_paths = apr_hash_make(result_pool);
10896 /* First check if LOCAL_RELPATH itself has iprops */
10897 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10898 STMT_SELECT_IPROPS_NODE));
10899 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10900 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10904 const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10906 const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10907 relpath_with_cache,
10909 svn_hash_sets(*iprop_paths, abspath_with_cache,
10910 svn_sqlite__column_text(stmt, 1, result_pool));
10912 SVN_ERR(svn_sqlite__reset(stmt));
10914 if (depth == svn_depth_empty)
10915 return SVN_NO_ERROR;
10917 /* Now fetch information for children or all descendants */
10918 if (depth == svn_depth_files
10919 || depth == svn_depth_immediates)
10921 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10922 STMT_SELECT_IPROPS_CHILDREN));
10924 else /* Default to svn_depth_infinity. */
10926 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10927 STMT_SELECT_IPROPS_RECURSIVE));
10930 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10931 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10935 const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10937 const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10938 relpath_with_cache,
10940 svn_hash_sets(*iprop_paths, abspath_with_cache,
10941 svn_sqlite__column_text(stmt, 1, result_pool));
10942 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10945 SVN_ERR(svn_sqlite__reset(stmt));
10947 /* For depth files we should filter non files */
10948 if (depth == svn_depth_files)
10950 apr_hash_index_t *hi;
10951 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
10953 for (hi = apr_hash_first(scratch_pool, *iprop_paths);
10955 hi = apr_hash_next(hi))
10957 const char *child_abspath = apr_hash_this_key(hi);
10958 const char *child_relpath;
10959 svn_node_kind_t child_kind;
10961 svn_pool_clear(iterpool);
10963 child_relpath = svn_dirent_is_child(local_relpath, child_abspath,
10966 if (! child_relpath)
10968 continue; /* local_relpath itself */
10971 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &child_kind, NULL,
10972 NULL, NULL, NULL, NULL,
10973 NULL, NULL, NULL, NULL,
10974 NULL, NULL, NULL, NULL,
10975 wcroot, child_relpath,
10979 /* Filter if not a file */
10980 if (child_kind != svn_node_file)
10982 svn_hash_sets(*iprop_paths, child_abspath, NULL);
10986 svn_pool_destroy(iterpool);
10989 return SVN_NO_ERROR;
10993 svn_wc__db_get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10995 const char *local_abspath,
10997 apr_pool_t *result_pool,
10998 apr_pool_t *scratch_pool)
11000 svn_wc__db_wcroot_t *wcroot;
11001 const char *local_relpath;
11003 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11005 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11006 local_abspath, scratch_pool,
11008 VERIFY_USABLE_WCROOT(wcroot);
11010 SVN_WC__DB_WITH_TXN(
11011 get_children_with_cached_iprops(iprop_paths, wcroot, local_relpath,
11012 depth, result_pool, scratch_pool),
11015 return SVN_NO_ERROR;
11019 svn_wc__db_read_children_of_working_node(const apr_array_header_t **children,
11021 const char *local_abspath,
11022 apr_pool_t *result_pool,
11023 apr_pool_t *scratch_pool)
11025 svn_wc__db_wcroot_t *wcroot;
11026 const char *local_relpath;
11028 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11030 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11032 scratch_pool, scratch_pool));
11033 VERIFY_USABLE_WCROOT(wcroot);
11035 return svn_error_trace(
11036 gather_children(children, wcroot, local_relpath,
11037 STMT_SELECT_WORKING_CHILDREN, -1,
11038 result_pool, scratch_pool));
11042 svn_wc__db_base_read_not_present_children(
11043 const apr_array_header_t **children,
11045 const char *local_abspath,
11046 apr_pool_t *result_pool,
11047 apr_pool_t *scratch_pool)
11049 svn_wc__db_wcroot_t *wcroot;
11050 const char *local_relpath;
11052 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11054 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11056 scratch_pool, scratch_pool));
11057 VERIFY_USABLE_WCROOT(wcroot);
11059 return svn_error_trace(
11060 gather_children(children, wcroot, local_relpath,
11061 STMT_SELECT_BASE_NOT_PRESENT_CHILDREN, -1,
11062 result_pool, scratch_pool));
11065 /* Helper for svn_wc__db_node_check_replace().
11067 static svn_error_t *
11068 check_replace_txn(svn_boolean_t *is_replace_root_p,
11069 svn_boolean_t *base_replace_p,
11070 svn_boolean_t *is_replace_p,
11071 svn_wc__db_wcroot_t *wcroot,
11072 const char *local_relpath,
11073 apr_pool_t *scratch_pool)
11075 svn_sqlite__stmt_t *stmt;
11076 svn_boolean_t have_row;
11077 svn_boolean_t is_replace = FALSE;
11078 int replaced_op_depth;
11079 svn_wc__db_status_t replaced_status;
11081 /* Our caller initialized the output values to FALSE */
11083 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11084 STMT_SELECT_NODE_INFO));
11086 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11088 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11091 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
11092 svn_sqlite__reset(stmt),
11093 _("The node '%s' was not found."),
11094 path_for_error_message(wcroot, local_relpath,
11098 svn_wc__db_status_t status;
11100 status = svn_sqlite__column_token(stmt, 3, presence_map);
11102 if (status != svn_wc__db_status_normal)
11103 return svn_error_trace(svn_sqlite__reset(stmt));
11106 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11109 return svn_error_trace(svn_sqlite__reset(stmt));
11111 replaced_status = svn_sqlite__column_token(stmt, 3, presence_map);
11113 /* If the layer below the add describes a not present or a deleted node,
11114 this is not a replacement. Deleted can only occur if an ancestor is
11115 the delete root. */
11116 if (replaced_status != svn_wc__db_status_not_present
11117 && replaced_status != svn_wc__db_status_excluded
11118 && replaced_status != svn_wc__db_status_server_excluded
11119 && replaced_status != svn_wc__db_status_base_deleted)
11123 *is_replace_p = TRUE;
11126 replaced_op_depth = svn_sqlite__column_int(stmt, 0);
11128 if (base_replace_p)
11130 int op_depth = svn_sqlite__column_int(stmt, 0);
11132 while (op_depth != 0 && have_row)
11134 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11137 op_depth = svn_sqlite__column_int(stmt, 0);
11140 if (have_row && op_depth == 0)
11142 svn_wc__db_status_t base_status;
11144 base_status = svn_sqlite__column_token(stmt, 3, presence_map);
11146 *base_replace_p = (base_status != svn_wc__db_status_not_present);
11150 SVN_ERR(svn_sqlite__reset(stmt));
11152 if (!is_replace_root_p || !is_replace)
11153 return SVN_NO_ERROR;
11155 if (replaced_status != svn_wc__db_status_base_deleted)
11157 int parent_op_depth;
11159 /* Check the current op-depth of the parent to see if we are a replacement
11161 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
11162 svn_relpath_dirname(local_relpath,
11165 SVN_ERR(svn_sqlite__step_row(stmt)); /* Parent must exist as 'normal' */
11167 parent_op_depth = svn_sqlite__column_int(stmt, 0);
11169 if (parent_op_depth >= replaced_op_depth)
11171 /* Did we replace inside our directory? */
11173 *is_replace_root_p = (parent_op_depth == replaced_op_depth);
11174 SVN_ERR(svn_sqlite__reset(stmt));
11175 return SVN_NO_ERROR;
11178 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11181 parent_op_depth = svn_sqlite__column_int(stmt, 0);
11183 SVN_ERR(svn_sqlite__reset(stmt));
11186 *is_replace_root_p = TRUE; /* Parent is no replacement */
11187 else if (parent_op_depth < replaced_op_depth)
11188 *is_replace_root_p = TRUE; /* Parent replaces a lower layer */
11189 /*else // No replacement root */
11192 return SVN_NO_ERROR;
11196 svn_wc__db_node_check_replace(svn_boolean_t *is_replace_root,
11197 svn_boolean_t *base_replace,
11198 svn_boolean_t *is_replace,
11200 const char *local_abspath,
11201 apr_pool_t *scratch_pool)
11203 svn_wc__db_wcroot_t *wcroot;
11204 const char *local_relpath;
11206 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11208 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11210 scratch_pool, scratch_pool));
11211 VERIFY_USABLE_WCROOT(wcroot);
11213 if (is_replace_root)
11214 *is_replace_root = FALSE;
11216 *base_replace = FALSE;
11218 *is_replace = FALSE;
11220 if (local_relpath[0] == '\0')
11221 return SVN_NO_ERROR; /* Working copy root can't be replaced */
11223 SVN_WC__DB_WITH_TXN(
11224 check_replace_txn(is_replace_root, base_replace, is_replace,
11225 wcroot, local_relpath, scratch_pool),
11228 return SVN_NO_ERROR;
11232 svn_wc__db_read_children(const apr_array_header_t **children,
11234 const char *local_abspath,
11235 apr_pool_t *result_pool,
11236 apr_pool_t *scratch_pool)
11238 svn_wc__db_wcroot_t *wcroot;
11239 const char *local_relpath;
11241 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11243 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11245 scratch_pool, scratch_pool));
11246 VERIFY_USABLE_WCROOT(wcroot);
11248 return gather_children(children, wcroot, local_relpath,
11249 STMT_SELECT_NODE_CHILDREN, -1,
11250 result_pool, scratch_pool);
11254 /* Implementation of svn_wc__db_global_relocate */
11255 static svn_error_t *
11256 relocate_txn(svn_wc__db_wcroot_t *wcroot,
11257 const char *local_relpath,
11258 const char *repos_root_url,
11259 apr_pool_t *scratch_pool)
11261 svn_sqlite__stmt_t *stmt;
11262 apr_int64_t new_repos_id;
11263 const char *local_dir_relpath;
11264 svn_wc__db_status_t status;
11265 const char *repos_uuid;
11266 svn_boolean_t have_base_node;
11267 apr_int64_t old_repos_id;
11269 local_dir_relpath = local_relpath;
11271 SVN_ERR(read_info(&status,
11272 NULL, NULL, NULL, &old_repos_id,
11273 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11274 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11276 &have_base_node, NULL, NULL,
11277 wcroot, local_relpath,
11278 scratch_pool, scratch_pool));
11280 if (status == svn_wc__db_status_excluded)
11282 /* The parent cannot be excluded, so look at the parent and then
11283 adjust the relpath */
11284 const char *parent_relpath = svn_relpath_dirname(local_dir_relpath,
11286 SVN_ERR(read_info(&status,
11287 NULL, NULL, NULL, &old_repos_id,
11288 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11289 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11292 wcroot, parent_relpath,
11293 scratch_pool, scratch_pool));
11294 local_dir_relpath = parent_relpath;
11297 if (old_repos_id == INVALID_REPOS_ID)
11299 /* Do we need to support relocating something that is
11300 added/deleted/excluded without relocating the parent? If not
11301 then perhaps relpath, root_url and uuid should be passed down
11302 to the children so that they don't have to scan? */
11304 if (status == svn_wc__db_status_deleted)
11306 const char *work_del_relpath;
11308 SVN_ERR(scan_deletion(NULL, NULL,
11309 &work_del_relpath, NULL,
11310 wcroot, local_dir_relpath,
11313 if (work_del_relpath)
11315 /* Deleted within a copy/move */
11317 /* The parent of the delete is added. */
11318 status = svn_wc__db_status_added;
11319 local_dir_relpath = svn_relpath_dirname(work_del_relpath,
11324 if (status == svn_wc__db_status_added)
11326 SVN_ERR(scan_addition(NULL, NULL, NULL, &old_repos_id,
11327 NULL, NULL, NULL, NULL, NULL, NULL,
11328 wcroot, local_dir_relpath,
11329 scratch_pool, scratch_pool));
11332 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL,
11334 NULL, NULL, NULL, NULL, NULL,
11335 NULL, NULL, NULL, NULL, NULL,
11336 wcroot, local_dir_relpath,
11337 scratch_pool, scratch_pool));
11340 SVN_ERR(svn_wc__db_fetch_repos_info(NULL, &repos_uuid, wcroot,
11341 old_repos_id, scratch_pool));
11342 SVN_ERR_ASSERT(repos_uuid);
11344 /* This function affects all the children of the given local_relpath,
11345 but the way that it does this is through the repos inheritance mechanism.
11346 So, we only need to rewrite the repos_id of the given local_relpath,
11347 as well as any children with a non-null repos_id, as well as various
11348 repos_id fields in the locks and working_node tables.
11351 /* Get the repos_id for the new repository. */
11352 SVN_ERR(create_repos_id(&new_repos_id, repos_root_url, repos_uuid,
11353 wcroot->sdb, scratch_pool));
11355 /* Set the (base and working) repos_ids and clear the dav_caches */
11356 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11357 STMT_RECURSIVE_UPDATE_NODE_REPO));
11358 SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
11359 old_repos_id, new_repos_id));
11360 SVN_ERR(svn_sqlite__step_done(stmt));
11362 if (have_base_node)
11364 /* Update any locks for the root or its children. */
11365 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11366 STMT_UPDATE_LOCK_REPOS_ID));
11367 SVN_ERR(svn_sqlite__bindf(stmt, "ii", old_repos_id, new_repos_id));
11368 SVN_ERR(svn_sqlite__step_done(stmt));
11371 /* ### TODO: Update urls stored in inherited properties...
11372 What about urls in conflicts?
11373 # We can probably keep these as they are only used
11374 for showing full urls to the user */
11376 return SVN_NO_ERROR;
11381 svn_wc__db_global_relocate(svn_wc__db_t *db,
11382 const char *local_dir_abspath,
11383 const char *repos_root_url,
11384 apr_pool_t *scratch_pool)
11386 svn_wc__db_wcroot_t *wcroot;
11387 const char *local_relpath;
11389 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
11390 /* ### assert that we were passed a directory? */
11392 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
11393 db, local_dir_abspath, scratch_pool, scratch_pool));
11394 VERIFY_USABLE_WCROOT(wcroot);
11396 SVN_WC__DB_WITH_TXN(
11397 relocate_txn(wcroot, local_relpath, repos_root_url, scratch_pool),
11400 SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_infinity,
11403 return SVN_NO_ERROR;
11407 /* Helper for commit_node()
11408 Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of
11409 (WCROOT, LOCAL_RELPATH), directly if its BASE row exists or implied from
11410 its parent's BASE row if not. In the latter case, error if the parent
11411 BASE row does not exist. */
11412 static svn_error_t *
11413 determine_commit_repos_info(apr_int64_t *repos_id,
11414 const char **repos_relpath,
11415 svn_wc__db_wcroot_t *wcroot,
11416 const char *local_relpath,
11417 apr_pool_t *result_pool,
11418 apr_pool_t *scratch_pool)
11420 svn_sqlite__stmt_t *stmt;
11421 svn_boolean_t have_row;
11424 /* Prefer the current node's repository information. */
11425 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11426 STMT_SELECT_NODE_INFO));
11427 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11428 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11431 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
11432 svn_sqlite__reset(stmt),
11433 _("The node '%s' was not found."),
11434 path_for_error_message(wcroot, local_relpath,
11437 op_depth = svn_sqlite__column_int(stmt, 0);
11441 svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 3,
11444 if (presence == svn_wc__db_status_base_deleted)
11446 SVN_ERR(svn_sqlite__step_row(stmt)); /* There must be a row */
11447 op_depth = svn_sqlite__column_int(stmt, 0);
11451 const char *parent_repos_relpath;
11452 const char *parent_relpath;
11455 SVN_ERR(svn_sqlite__reset(stmt));
11457 /* The repository relative path of an add/copy is based on its
11458 ancestor, not on the shadowed base layer.
11460 As this function is only used from the commit processing we know
11461 the parent directory has only a BASE row, so we can just obtain
11462 the information directly by recursing (once!) */
11464 svn_relpath_split(&parent_relpath, &name, local_relpath,
11467 SVN_ERR(determine_commit_repos_info(repos_id, &parent_repos_relpath,
11468 wcroot, parent_relpath,
11469 scratch_pool, scratch_pool));
11471 *repos_relpath = svn_relpath_join(parent_repos_relpath, name,
11473 return SVN_NO_ERROR;
11478 SVN_ERR_ASSERT(op_depth == 0); /* And that row must be BASE */
11480 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1));
11481 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 2));
11483 *repos_id = svn_sqlite__column_int64(stmt, 1);
11484 *repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
11486 return svn_error_trace(svn_sqlite__reset(stmt));
11489 static svn_error_t *
11490 moved_descendant_collect(apr_hash_t **map,
11491 svn_wc__db_wcroot_t *wcroot,
11492 const char *local_relpath,
11494 apr_pool_t *result_pool,
11495 apr_pool_t *scratch_pool)
11497 svn_sqlite__stmt_t *stmt;
11498 svn_boolean_t have_row;
11502 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11503 STMT_SELECT_MOVED_DESCENDANTS_SRC));
11504 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
11508 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11510 return svn_error_trace(svn_sqlite__reset(stmt));
11512 /* Find all moved descendants. Key them on target, because that is
11516 const char *src_relpath = svn_sqlite__column_text(stmt, 1, result_pool);
11517 const char *to_relpath = svn_sqlite__column_text(stmt, 4, result_pool);
11520 *map = apr_hash_make(result_pool);
11522 svn_hash_sets(*map, to_relpath, src_relpath);
11524 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11526 SVN_ERR(svn_sqlite__reset(stmt));
11528 return SVN_NO_ERROR;
11531 /* Helper for svn_wc__db_global_commit()
11533 Makes local_relpath and all its descendants at the same op-depth represent
11534 the copy origin repos_id:repos_relpath@revision.
11536 This code is only valid to fix-up a move from an old location, to a new
11537 location during a commit.
11540 * local_relpath is not the working copy root (can't be moved)
11541 * repos_relpath is not the repository root (can't be moved)
11543 static svn_error_t *
11544 moved_descendant_commit(svn_wc__db_wcroot_t *wcroot,
11545 const char *local_relpath,
11546 apr_int64_t repos_id,
11547 const char *repos_relpath,
11548 svn_revnum_t revision,
11549 apr_hash_t *children,
11550 apr_pool_t *scratch_pool)
11552 apr_pool_t *iterpool;
11553 svn_sqlite__stmt_t *stmt;
11554 apr_hash_index_t *hi;
11556 SVN_ERR_ASSERT(*local_relpath != '\0'
11557 && *repos_relpath != '\0');
11560 return SVN_NO_ERROR;
11562 /* Then update them */
11563 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11564 STMT_COMMIT_UPDATE_ORIGIN));
11566 iterpool = svn_pool_create(scratch_pool);
11567 for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
11569 const char *src_relpath = apr_hash_this_val(hi);
11570 const char *to_relpath = apr_hash_this_key(hi);
11571 const char *new_repos_relpath;
11572 int to_op_depth = relpath_depth(to_relpath);
11576 svn_pool_clear(iterpool);
11578 SVN_ERR_ASSERT(to_op_depth > 0);
11580 new_repos_relpath = svn_relpath_join(
11582 svn_relpath_skip_ancestor(local_relpath,
11586 SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11592 SVN_ERR(svn_sqlite__update(&affected, stmt));
11595 /* Enable in release code?
11596 Broken moves are not fatal yet, but this assertion would break
11598 SVN_ERR_ASSERT(affected >= 1); /* If this fails there is no move dest */
11601 SVN_ERR(moved_descendant_collect(&map, wcroot, to_relpath, to_op_depth,
11602 iterpool, iterpool));
11603 SVN_ERR(moved_descendant_commit(wcroot, to_relpath,
11604 repos_id, new_repos_relpath, revision,
11608 svn_pool_destroy(iterpool);
11609 return SVN_NO_ERROR;
11612 /* Helper for svn_wc__db_global_commit()
11614 Moves all nodes below LOCAL_RELPATH from op-depth OP_DEPTH to op-depth 0
11615 (BASE), setting their presence to 'not-present' if their presence wasn't
11618 Makes all nodes below LOCAL_RELPATH represent the descendants of repository
11619 location repos_id:repos_relpath@revision.
11622 * local_relpath is not the working copy root (can't be replaced)
11623 * repos_relpath is not the repository root (can't be replaced)
11625 static svn_error_t *
11626 descendant_commit(svn_wc__db_wcroot_t *wcroot,
11627 const char *local_relpath,
11629 apr_int64_t repos_id,
11630 const char *repos_relpath,
11631 svn_revnum_t revision,
11632 apr_pool_t *scratch_pool)
11634 svn_sqlite__stmt_t *stmt;
11636 SVN_ERR_ASSERT(*local_relpath != '\0'
11637 && *repos_relpath != '\0');
11639 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11640 STMT_COMMIT_DESCENDANTS_TO_BASE));
11642 SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11649 SVN_ERR(svn_sqlite__update(NULL, stmt));
11651 return SVN_NO_ERROR;
11654 /* The body of svn_wc__db_global_commit().
11656 static svn_error_t *
11657 commit_node(svn_wc__db_wcroot_t *wcroot,
11658 const char *local_relpath,
11659 svn_revnum_t new_revision,
11660 svn_revnum_t changed_rev,
11661 apr_time_t changed_date,
11662 const char *changed_author,
11663 const svn_checksum_t *new_checksum,
11664 apr_hash_t *new_dav_cache,
11665 svn_boolean_t keep_changelist,
11666 svn_boolean_t no_unlock,
11667 const svn_skel_t *work_items,
11668 apr_pool_t *scratch_pool)
11670 svn_sqlite__stmt_t *stmt_info;
11671 svn_sqlite__stmt_t *stmt_act;
11672 svn_boolean_t have_act;
11673 svn_string_t prop_blob = { 0 };
11674 svn_string_t inherited_prop_blob = { 0 };
11675 const char *changelist = NULL;
11676 const char *parent_relpath;
11677 svn_wc__db_status_t new_presence;
11678 svn_node_kind_t new_kind;
11679 const char *new_depth_str = NULL;
11680 svn_sqlite__stmt_t *stmt;
11681 apr_int64_t repos_id;
11682 const char *repos_relpath;
11684 svn_wc__db_status_t old_presence;
11685 svn_boolean_t moved_here;
11687 /* If we are adding a file or directory, then we need to get
11688 repository information from the parent node since "this node" does
11691 For existing nodes, we should retain the (potentially-switched)
11692 repository information. */
11693 SVN_ERR(determine_commit_repos_info(&repos_id, &repos_relpath,
11694 wcroot, local_relpath,
11695 scratch_pool, scratch_pool));
11697 /* ### is it better to select only the data needed? */
11698 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
11699 STMT_SELECT_NODE_INFO));
11700 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
11701 SVN_ERR(svn_sqlite__step_row(stmt_info));
11703 SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
11704 STMT_SELECT_ACTUAL_NODE));
11705 SVN_ERR(svn_sqlite__bindf(stmt_act, "is",
11706 wcroot->wc_id, local_relpath));
11707 SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
11709 /* There should be something to commit! */
11711 op_depth = svn_sqlite__column_int(stmt_info, 0);
11713 /* Figure out the new node's kind. It will be whatever is in WORKING_NODE,
11714 or there will be a BASE_NODE that has it. */
11715 old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map);
11716 new_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
11718 /* What will the new depth be? */
11719 if (new_kind == svn_node_dir)
11720 new_depth_str = svn_sqlite__column_text(stmt_info, 11, scratch_pool);
11722 /* Check that the repository information is not being changed. */
11725 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 1));
11726 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 2));
11728 /* A commit cannot change these values. */
11729 SVN_ERR_ASSERT(repos_id == svn_sqlite__column_int64(stmt_info, 1));
11730 SVN_ERR_ASSERT(strcmp(repos_relpath,
11731 svn_sqlite__column_text(stmt_info, 2, NULL)) == 0);
11734 if (old_presence != svn_wc__db_status_base_deleted)
11736 /* Find the appropriate new properties -- ACTUAL overrides any properties
11737 in WORKING that arrived as part of a copy/move.
11739 Note: we'll keep them as a big blob of data, rather than
11740 deserialize/serialize them. */
11742 prop_blob.data = svn_sqlite__column_blob(stmt_act, 1, &prop_blob.len,
11744 if (prop_blob.data == NULL)
11745 prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len,
11748 inherited_prop_blob.data = svn_sqlite__column_blob(
11750 &inherited_prop_blob.len,
11753 if (keep_changelist && have_act)
11754 changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool);
11756 moved_here = svn_sqlite__column_int(stmt_info, 15);
11760 moved_here = FALSE;
11764 /* ### other stuff? */
11766 SVN_ERR(svn_sqlite__reset(stmt_info));
11767 SVN_ERR(svn_sqlite__reset(stmt_act));
11773 SVN_ERR_ASSERT(op_depth == relpath_depth(local_relpath));
11775 /* First clear the moves that we are going to delete in a bit */
11777 apr_hash_t *old_moves;
11778 apr_hash_index_t *hi;
11779 SVN_ERR(moved_descendant_collect(&old_moves, wcroot, local_relpath, 0,
11780 scratch_pool, scratch_pool));
11783 for (hi = apr_hash_first(scratch_pool, old_moves);
11784 hi; hi = apr_hash_next(hi))
11786 SVN_ERR(clear_moved_here(wcroot, apr_hash_this_key(hi),
11791 /* This removes all layers of this node and at the same time determines
11792 if we need to remove shadowed layers below our descendants. */
11794 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11795 STMT_DELETE_NODE_ALL_LAYERS));
11796 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11797 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
11799 if (affected_rows > 1)
11801 /* We commit a shadowing operation
11803 1) Remove all shadowed nodes
11804 2) And remove all nodes that have a base-deleted as lowest layer,
11805 because 1) removed that layer */
11807 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11808 STMT_DELETE_SHADOWED_RECURSIVE));
11810 SVN_ERR(svn_sqlite__bindf(stmt,
11816 SVN_ERR(svn_sqlite__step_done(stmt));
11819 /* Note that while these two calls look so similar that they might
11820 be integrated, they really affect a different op-depth and
11821 completely different nodes (via a different recursion pattern). */
11823 if (old_presence != svn_wc__db_status_base_deleted)
11825 /* Collapse descendants of the current op_depth to layer 0,
11826 this includes moved-from/to clearing */
11827 SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth,
11828 repos_id, repos_relpath, new_revision,
11832 if (old_presence != svn_wc__db_status_base_deleted)
11834 apr_hash_t *moves = NULL;
11836 SVN_ERR(moved_descendant_collect(&moves, wcroot, local_relpath, 0,
11837 scratch_pool, scratch_pool));
11839 /* And make the recorded local moves represent moves of the node we
11841 SVN_ERR(moved_descendant_commit(wcroot, local_relpath,
11842 repos_id, repos_relpath, new_revision,
11843 moves, scratch_pool));
11848 /* This node is no longer modified, so no node was moved here */
11849 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11850 STMT_CLEAR_MOVED_TO_FROM_DEST));
11851 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
11854 SVN_ERR(svn_sqlite__step_done(stmt));
11857 /* Update or add the BASE_NODE row with all the new information. */
11859 if (*local_relpath == '\0')
11860 parent_relpath = NULL;
11862 parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
11864 /* Preserve any incomplete status */
11865 if (old_presence != svn_wc__db_status_base_deleted)
11867 new_presence = (old_presence == svn_wc__db_status_incomplete
11868 ? svn_wc__db_status_incomplete
11869 : svn_wc__db_status_normal);
11871 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11872 STMT_APPLY_CHANGES_TO_BASE_NODE));
11873 /* symlink_target not yet used */
11874 SVN_ERR(svn_sqlite__bindf(stmt, "issisrtstrisnbn",
11875 wcroot->wc_id, local_relpath,
11880 presence_map, new_presence,
11882 kind_map, new_kind,
11886 prop_blob.data, prop_blob.len));
11888 SVN_ERR(svn_sqlite__bind_checksum(stmt, 13, new_checksum,
11890 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, new_dav_cache,
11892 if (inherited_prop_blob.data != NULL)
11894 SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data,
11895 inherited_prop_blob.len));
11898 SVN_ERR(svn_sqlite__step_done(stmt));
11902 struct insert_base_baton_t ibb;
11905 ibb.repos_id = repos_id;
11906 ibb.status = svn_wc__db_status_not_present;
11907 ibb.kind = new_kind;
11908 ibb.repos_relpath = repos_relpath;
11909 ibb.revision = new_revision;
11911 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
11913 keep_changelist = FALSE; /* Nothing there */
11918 if (keep_changelist && changelist != NULL)
11920 /* The user told us to keep the changelist. Replace the row in
11921 ACTUAL_NODE with the basic keys and the changelist. */
11922 SVN_ERR(svn_sqlite__get_statement(
11923 &stmt, wcroot->sdb,
11924 STMT_RESET_ACTUAL_WITH_CHANGELIST));
11925 SVN_ERR(svn_sqlite__bindf(stmt, "isss",
11926 wcroot->wc_id, local_relpath,
11927 svn_relpath_dirname(local_relpath,
11930 SVN_ERR(svn_sqlite__step_done(stmt));
11934 /* Toss the ACTUAL_NODE row. */
11935 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11936 STMT_DELETE_ACTUAL_NODE));
11937 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11938 SVN_ERR(svn_sqlite__step_done(stmt));
11944 svn_sqlite__stmt_t *lock_stmt;
11945 svn_boolean_t op_root = (op_depth > 0
11946 && (relpath_depth(local_relpath) == op_depth));
11948 /* If we are committing an add of a delete, we can assume we own
11949 all locks at or below REPOS_RELPATH (or the server would have
11950 denied the commit). As we must have passed these to the server
11951 we can now safely remove them.
11953 SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
11955 ? STMT_DELETE_LOCK_RECURSIVELY
11956 : STMT_DELETE_LOCK));
11957 SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
11958 SVN_ERR(svn_sqlite__step_done(lock_stmt));
11961 /* Install any work items into the queue, as part of this transaction. */
11962 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
11964 return SVN_NO_ERROR;
11969 svn_wc__db_global_commit(svn_wc__db_t *db,
11970 const char *local_abspath,
11971 svn_revnum_t new_revision,
11972 svn_revnum_t changed_revision,
11973 apr_time_t changed_date,
11974 const char *changed_author,
11975 const svn_checksum_t *new_checksum,
11976 apr_hash_t *new_dav_cache,
11977 svn_boolean_t keep_changelist,
11978 svn_boolean_t no_unlock,
11979 const svn_skel_t *work_items,
11980 apr_pool_t *scratch_pool)
11982 const char *local_relpath;
11983 svn_wc__db_wcroot_t *wcroot;
11985 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11986 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
11988 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11989 local_abspath, scratch_pool, scratch_pool));
11990 VERIFY_USABLE_WCROOT(wcroot);
11992 SVN_WC__DB_WITH_TXN(
11993 commit_node(wcroot, local_relpath,
11994 new_revision, changed_revision, changed_date, changed_author,
11995 new_checksum, new_dav_cache, keep_changelist,
11996 no_unlock, work_items, scratch_pool),
11999 /* We *totally* monkeyed the entries. Toss 'em. */
12000 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
12002 return SVN_NO_ERROR;
12007 svn_wc__db_global_update(svn_wc__db_t *db,
12008 const char *local_abspath,
12009 svn_node_kind_t new_kind,
12010 const char *new_repos_relpath,
12011 svn_revnum_t new_revision,
12012 const apr_hash_t *new_props,
12013 svn_revnum_t new_changed_rev,
12014 apr_time_t new_changed_date,
12015 const char *new_changed_author,
12016 const apr_array_header_t *new_children,
12017 const svn_checksum_t *new_checksum,
12018 const char *new_target,
12019 const apr_hash_t *new_dav_cache,
12020 const svn_skel_t *conflict,
12021 const svn_skel_t *work_items,
12022 apr_pool_t *scratch_pool)
12027 svn_wc__db_wcroot_t *wcroot;
12028 const char *local_relpath;
12030 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12031 /* ### allow NULL for NEW_REPOS_RELPATH to indicate "no change"? */
12032 SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
12033 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
12034 SVN_ERR_ASSERT(new_props != NULL);
12035 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_changed_rev));
12036 SVN_ERR_ASSERT((new_children != NULL
12037 && new_checksum == NULL
12038 && new_target == NULL)
12039 || (new_children == NULL
12040 && new_checksum != NULL
12041 && new_target == NULL)
12042 || (new_children == NULL
12043 && new_checksum == NULL
12044 && new_target != NULL));
12046 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12047 local_abspath, scratch_pool, scratch_pool));
12048 VERIFY_USABLE_WCROOT(wcroot);
12050 SVN_WC__DB_WITH_TXN(
12051 update_node(wcroot, local_relpath,
12052 new_repos_relpath, new_revision, new_props,
12053 new_changed_rev, new_changed_date, new_changed_author,
12054 new_children, new_checksum, new_target,
12055 conflict, work_items, scratch_pool),
12058 /* We *totally* monkeyed the entries. Toss 'em. */
12059 SVN_ERR(flush_entries(wcroot, local_abspath, scratch_pool));
12061 return SVN_NO_ERROR;
12065 /* Sets a base nodes revision, repository relative path, and/or inherited
12066 propertis. If LOCAL_ABSPATH's rev (REV) is valid, set its revision. If
12067 SET_REPOS_RELPATH is TRUE set its repository relative path to REPOS_RELPATH
12068 (and make sure its REPOS_ID is still valid). If IPROPS is not NULL set its
12069 inherited properties to IPROPS, if IPROPS is NULL then clear any the iprops
12070 cache for the base node.
12072 static svn_error_t *
12073 db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot,
12074 const char *local_relpath,
12075 apr_array_header_t *iprops,
12077 svn_boolean_t set_repos_relpath,
12078 const char *repos_relpath,
12079 apr_int64_t repos_id,
12080 apr_pool_t *scratch_pool)
12082 svn_sqlite__stmt_t *stmt;
12084 SVN_ERR(flush_entries(wcroot,
12085 svn_dirent_join(wcroot->abspath, local_relpath,
12087 svn_depth_empty, scratch_pool));
12090 if (SVN_IS_VALID_REVNUM(rev))
12092 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12093 STMT_UPDATE_BASE_REVISION));
12095 SVN_ERR(svn_sqlite__bindf(stmt, "isr", wcroot->wc_id, local_relpath,
12098 SVN_ERR(svn_sqlite__step_done(stmt));
12101 if (set_repos_relpath)
12103 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12104 STMT_UPDATE_BASE_REPOS));
12106 SVN_ERR(svn_sqlite__bindf(stmt, "isis", wcroot->wc_id, local_relpath,
12107 repos_id, repos_relpath));
12109 SVN_ERR(svn_sqlite__step_done(stmt));
12112 /* Set or clear iprops. */
12113 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12114 STMT_UPDATE_IPROP));
12115 SVN_ERR(svn_sqlite__bindf(stmt, "is",
12118 SVN_ERR(svn_sqlite__bind_iprops(stmt, 3, iprops, scratch_pool));
12119 SVN_ERR(svn_sqlite__step_done(stmt));
12121 return SVN_NO_ERROR;
12124 /* The main body of bump_revisions_post_update().
12126 * Tweak the information for LOCAL_RELPATH in WCROOT. If NEW_REPOS_RELPATH is
12127 * non-NULL update the entry to the new url specified by NEW_REPOS_RELPATH,
12128 * NEW_REPOS_ID. If NEW_REV is valid, make this the node's working revision.
12130 * NODE_STATUS, NODE_KIND, NODE_REVISION and NODE_REPOS_RELPATH represent the
12131 * values as stored currently in WCROOT for LOCAL_RELPATH.
12133 * If WCROOT_IPROPS is not NULL it is a hash mapping const char * absolute
12134 * working copy paths to depth-first ordered arrays of
12135 * svn_prop_inherited_item_t * structures. If the absolute path equivalent
12136 * of LOCAL_RELPATH exists in WCROOT_IPROPS, then set the hashed value as the
12137 * node's inherited properties.
12139 * Unless S_ROOT is TRUE the tweaks might cause the node for LOCAL_ABSPATH to
12140 * be removed from the WC; if IS_ROOT is TRUE this will not happen.
12142 static svn_error_t *
12143 bump_node_revision(svn_wc__db_wcroot_t *wcroot,
12144 const char *local_relpath,
12145 svn_wc__db_status_t node_status,
12146 svn_node_kind_t node_kind,
12147 svn_revnum_t node_revision,
12148 const char *node_repos_relpath,
12149 apr_int64_t new_repos_id,
12150 const char *new_repos_relpath,
12151 svn_revnum_t new_rev,
12153 apr_hash_t *exclude_relpaths,
12154 apr_hash_t *wcroot_iprops,
12155 svn_boolean_t is_root,
12156 svn_boolean_t skip_when_dir,
12158 apr_pool_t *scratch_pool)
12160 apr_pool_t *iterpool;
12161 apr_hash_t *children;
12162 apr_hash_index_t *hi;
12163 svn_boolean_t set_repos_relpath = FALSE;
12164 svn_depth_t depth_below_here = depth;
12165 apr_array_header_t *iprops = NULL;
12167 if (new_repos_relpath != NULL
12168 && strcmp(node_repos_relpath, new_repos_relpath))
12169 set_repos_relpath = TRUE;
12172 iprops = svn_hash_gets(wcroot_iprops,
12173 svn_dirent_join(wcroot->abspath, local_relpath,
12177 || set_repos_relpath
12178 || (SVN_IS_VALID_REVNUM(new_rev) && new_rev != node_revision))
12180 SVN_ERR(db_op_set_rev_repos_relpath_iprops(wcroot, local_relpath,
12189 if (depth <= svn_depth_empty
12190 || node_kind != svn_node_dir
12191 || node_status == svn_wc__db_status_server_excluded
12192 || node_status == svn_wc__db_status_excluded
12193 || node_status == svn_wc__db_status_not_present)
12194 return SVN_NO_ERROR;
12196 /* And now recurse over the children */
12198 depth_below_here = depth;
12200 if (depth == svn_depth_immediates || depth == svn_depth_files)
12201 depth_below_here = svn_depth_empty;
12203 iterpool = svn_pool_create(scratch_pool);
12205 SVN_ERR(base_get_children_info(&children, wcroot, local_relpath, 0,
12206 scratch_pool, iterpool));
12207 for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
12209 const char *child_basename = apr_hash_this_key(hi);
12210 const struct svn_wc__db_base_info_t *child_info;
12211 const char *child_local_relpath;
12212 const char *child_repos_relpath = NULL;
12214 svn_pool_clear(iterpool);
12216 child_info = apr_hash_this_val(hi);
12218 if (child_info->update_root && child_info->kind == svn_node_file)
12219 continue; /* Skip file externals */
12221 if (depth < svn_depth_immediates && child_info->kind == svn_node_dir)
12222 continue; /* Skip directories */
12224 child_local_relpath = svn_relpath_join(local_relpath, child_basename,
12227 /* Don't touch nodes that can't be touched via the exclude list */
12228 if (svn_hash_gets(exclude_relpaths, child_local_relpath))
12231 /* If the node is still marked 'not-present', then the server did not
12232 re-add it. So it's really gone in this revision, thus we remove the
12235 If the node is still marked 'server-excluded' and yet is not the same
12236 revision as new_rev, then the server did not re-add it, nor
12237 re-server-exclude it, so we can remove the node. */
12238 if (child_info->status == svn_wc__db_status_not_present
12239 || (child_info->status == svn_wc__db_status_server_excluded &&
12240 child_info->revnum != new_rev))
12242 svn_sqlite__stmt_t *stmt;
12243 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12244 STMT_DELETE_BASE_NODE));
12245 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, child_local_relpath));
12246 SVN_ERR(svn_sqlite__step_done(stmt));
12250 /* Derive the new URL for the current (child) entry */
12251 if (new_repos_relpath)
12252 child_repos_relpath = svn_relpath_join(new_repos_relpath,
12253 child_basename, iterpool);
12255 SVN_ERR(bump_node_revision(wcroot, child_local_relpath,
12256 child_info->status,
12258 child_info->revnum,
12259 child_info->repos_relpath,
12261 child_repos_relpath, new_rev,
12263 exclude_relpaths, wcroot_iprops,
12264 FALSE /* is_root */,
12265 (depth < svn_depth_immediates), db,
12270 svn_pool_destroy(iterpool);
12272 return SVN_NO_ERROR;
12275 /* Helper for svn_wc__db_op_bump_revisions_post_update().
12277 static svn_error_t *
12278 bump_revisions_post_update(svn_wc__db_wcroot_t *wcroot,
12279 const char *local_relpath,
12282 const char *new_repos_relpath,
12283 const char *new_repos_root_url,
12284 const char *new_repos_uuid,
12285 svn_revnum_t new_revision,
12286 apr_hash_t *exclude_relpaths,
12287 apr_hash_t *wcroot_iprops,
12288 svn_boolean_t empty_update,
12289 svn_wc_notify_func2_t notify_func,
12290 void *notify_baton,
12291 apr_pool_t *scratch_pool)
12293 svn_wc__db_status_t status;
12294 svn_node_kind_t kind;
12296 apr_int64_t new_repos_id = INVALID_REPOS_ID;
12297 svn_revnum_t revision;
12298 const char *repos_relpath;
12300 err = svn_wc__db_base_get_info_internal(&status, &kind, &revision,
12301 &repos_relpath, NULL,
12302 NULL, NULL, NULL, NULL, NULL, NULL,
12303 NULL, NULL, NULL, NULL,
12304 wcroot, local_relpath,
12305 scratch_pool, scratch_pool);
12306 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
12308 svn_error_clear(err);
12309 return SVN_NO_ERROR;
12316 case svn_wc__db_status_excluded:
12317 case svn_wc__db_status_server_excluded:
12318 case svn_wc__db_status_not_present:
12319 return SVN_NO_ERROR;
12321 /* Explicitly ignore other statii */
12326 if (new_repos_root_url != NULL)
12327 SVN_ERR(create_repos_id(&new_repos_id, new_repos_root_url,
12329 wcroot->sdb, scratch_pool));
12331 SVN_ERR(bump_node_revision(wcroot, local_relpath,
12332 status, kind, revision, repos_relpath,
12334 new_repos_relpath, new_revision,
12335 depth, exclude_relpaths,
12337 TRUE /* is_root */, FALSE, db,
12340 /* ### TODO: Use empty_update flag for change knowledge */
12341 SVN_ERR(svn_wc__db_bump_moved_away(wcroot, local_relpath, depth, db,
12344 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, SVN_INVALID_REVNUM,
12345 SVN_INVALID_REVNUM, notify_func,
12346 notify_baton, scratch_pool));
12348 return SVN_NO_ERROR;
12352 svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db,
12353 const char *local_abspath,
12355 const char *new_repos_relpath,
12356 const char *new_repos_root_url,
12357 const char *new_repos_uuid,
12358 svn_revnum_t new_revision,
12359 apr_hash_t *exclude_relpaths,
12360 apr_hash_t *wcroot_iprops,
12361 svn_boolean_t empty_update,
12362 svn_wc_notify_func2_t notify_func,
12363 void *notify_baton,
12364 apr_pool_t *scratch_pool)
12366 const char *local_relpath;
12367 svn_wc__db_wcroot_t *wcroot;
12369 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12370 local_abspath, scratch_pool, scratch_pool));
12372 VERIFY_USABLE_WCROOT(wcroot);
12374 if (svn_hash_gets(exclude_relpaths, local_relpath))
12375 return SVN_NO_ERROR;
12377 if (depth == svn_depth_unknown)
12378 depth = svn_depth_infinity;
12380 SVN_WC__DB_WITH_TXN(
12381 bump_revisions_post_update(wcroot, local_relpath, db,
12382 depth, new_repos_relpath, new_repos_root_url,
12383 new_repos_uuid, new_revision,
12384 exclude_relpaths, wcroot_iprops, empty_update,
12385 notify_func, notify_baton, scratch_pool),
12388 return SVN_NO_ERROR;
12391 /* The body of svn_wc__db_lock_add().
12393 static svn_error_t *
12394 lock_add_txn(svn_wc__db_wcroot_t *wcroot,
12395 const char *local_relpath,
12396 const svn_wc__db_lock_t *lock,
12397 apr_pool_t *scratch_pool)
12399 svn_sqlite__stmt_t *stmt;
12400 const char *repos_relpath;
12401 apr_int64_t repos_id;
12403 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12404 &repos_relpath, &repos_id,
12405 NULL, NULL, NULL, NULL, NULL,
12406 NULL, NULL, NULL, NULL, NULL,
12407 wcroot, local_relpath,
12408 scratch_pool, scratch_pool));
12410 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_LOCK));
12411 SVN_ERR(svn_sqlite__bindf(stmt, "iss",
12412 repos_id, repos_relpath, lock->token));
12414 if (lock->owner != NULL)
12415 SVN_ERR(svn_sqlite__bind_text(stmt, 4, lock->owner));
12417 if (lock->comment != NULL)
12418 SVN_ERR(svn_sqlite__bind_text(stmt, 5, lock->comment));
12420 if (lock->date != 0)
12421 SVN_ERR(svn_sqlite__bind_int64(stmt, 6, lock->date));
12423 SVN_ERR(svn_sqlite__insert(NULL, stmt));
12425 return SVN_NO_ERROR;
12430 svn_wc__db_lock_add(svn_wc__db_t *db,
12431 const char *local_abspath,
12432 const svn_wc__db_lock_t *lock,
12433 apr_pool_t *scratch_pool)
12435 svn_wc__db_wcroot_t *wcroot;
12436 const char *local_relpath;
12438 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12439 SVN_ERR_ASSERT(lock != NULL);
12441 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12442 local_abspath, scratch_pool, scratch_pool));
12443 VERIFY_USABLE_WCROOT(wcroot);
12445 SVN_WC__DB_WITH_TXN(
12446 lock_add_txn(wcroot, local_relpath, lock, scratch_pool),
12449 /* There may be some entries, and the lock info is now out of date. */
12450 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
12452 return SVN_NO_ERROR;
12456 /* The body of svn_wc__db_lock_remove().
12458 static svn_error_t *
12459 lock_remove_txn(svn_wc__db_wcroot_t *wcroot,
12460 const char *local_relpath,
12461 svn_skel_t *work_items,
12462 apr_pool_t *scratch_pool)
12464 const char *repos_relpath;
12465 apr_int64_t repos_id;
12466 svn_sqlite__stmt_t *stmt;
12468 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12469 &repos_relpath, &repos_id,
12470 NULL, NULL, NULL, NULL, NULL,
12471 NULL, NULL, NULL, NULL, NULL,
12472 wcroot, local_relpath,
12473 scratch_pool, scratch_pool));
12475 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12476 STMT_DELETE_LOCK));
12477 SVN_ERR(svn_sqlite__bindf(stmt, "is", repos_id, repos_relpath));
12479 SVN_ERR(svn_sqlite__step_done(stmt));
12481 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
12483 return SVN_NO_ERROR;
12488 svn_wc__db_lock_remove(svn_wc__db_t *db,
12489 const char *local_abspath,
12490 svn_skel_t *work_items,
12491 apr_pool_t *scratch_pool)
12493 svn_wc__db_wcroot_t *wcroot;
12494 const char *local_relpath;
12496 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12498 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12499 local_abspath, scratch_pool, scratch_pool));
12500 VERIFY_USABLE_WCROOT(wcroot);
12502 SVN_WC__DB_WITH_TXN(
12503 lock_remove_txn(wcroot, local_relpath, work_items, scratch_pool),
12506 /* There may be some entries, and the lock info is now out of date. */
12507 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
12509 return SVN_NO_ERROR;
12512 /* A helper for scan_addition().
12513 * Compute moved-from information for the node at LOCAL_RELPATH which
12514 * has been determined as having been moved-here.
12515 * If MOVED_FROM_RELPATH is not NULL, set *MOVED_FROM_RELPATH to the
12516 * path of the move-source node in *MOVED_FROM_RELPATH.
12517 * If DELETE_OP_ROOT_RELPATH is not NULL, set *DELETE_OP_ROOT_RELPATH
12518 * to the path of the op-root of the delete-half of the move.
12519 * If moved-from information cannot be derived, set both *MOVED_FROM_RELPATH
12520 * and *DELETE_OP_ROOT_RELPATH to NULL, and return a "copied" status.
12521 * COPY_OPT_ROOT_RELPATH is the relpath of the op-root of the copied-half
12523 static svn_error_t *
12524 get_moved_from_info(const char **moved_from_relpath,
12525 const char **moved_from_op_root_relpath,
12526 const char *moved_to_op_root_relpath,
12528 svn_wc__db_wcroot_t *wcroot,
12529 const char *local_relpath,
12530 apr_pool_t *result_pool,
12531 apr_pool_t *scratch_pool)
12533 svn_sqlite__stmt_t *stmt;
12534 svn_boolean_t have_row;
12536 /* Run a query to get the moved-from path from the DB. */
12537 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12538 STMT_SELECT_MOVED_FROM_RELPATH));
12539 SVN_ERR(svn_sqlite__bindf(stmt, "is",
12540 wcroot->wc_id, moved_to_op_root_relpath));
12541 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12545 /* The move was only recorded at the copy-half, possibly because
12546 * the move operation was interrupted mid-way between the copy
12547 * and the delete. Treat this node as a normal copy. */
12548 if (moved_from_relpath)
12549 *moved_from_relpath = NULL;
12550 if (moved_from_op_root_relpath)
12551 *moved_from_op_root_relpath = NULL;
12553 SVN_ERR(svn_sqlite__reset(stmt));
12554 return SVN_NO_ERROR;
12558 *op_depth = svn_sqlite__column_int(stmt, 1);
12560 if (moved_from_relpath || moved_from_op_root_relpath)
12562 const char *db_delete_op_root_relpath;
12564 /* The moved-from path from the DB is the relpath of
12565 * the op_root of the delete-half of the move. */
12566 db_delete_op_root_relpath = svn_sqlite__column_text(stmt, 0,
12568 if (moved_from_op_root_relpath)
12569 *moved_from_op_root_relpath = db_delete_op_root_relpath;
12571 if (moved_from_relpath)
12573 if (strcmp(moved_to_op_root_relpath, local_relpath) == 0)
12575 /* LOCAL_RELPATH is the op_root of the copied-half of the
12576 * move, so the correct MOVED_FROM_ABSPATH is the op-root
12577 * of the delete-half. */
12578 *moved_from_relpath = db_delete_op_root_relpath;
12582 const char *child_relpath;
12584 /* LOCAL_RELPATH is a child that was copied along with the
12585 * op_root of the copied-half of the move. Construct the
12586 * corresponding path beneath the op_root of the delete-half. */
12588 /* Grab the child path relative to the op_root of the move
12590 child_relpath = svn_relpath_skip_ancestor(
12591 moved_to_op_root_relpath, local_relpath);
12593 SVN_ERR_ASSERT(child_relpath && strlen(child_relpath) > 0);
12595 /* This join is valid because LOCAL_RELPATH has not been moved
12596 * within the copied-half of the move yet -- else, it would
12597 * be its own op_root. */
12598 *moved_from_relpath = svn_relpath_join(db_delete_op_root_relpath,
12605 SVN_ERR(svn_sqlite__reset(stmt));
12607 return SVN_NO_ERROR;
12610 /* Like svn_wc__db_scan_addition(), but with WCROOT+LOCAL_RELPATH instead of
12613 The output value of *ORIGINAL_REPOS_ID will be INVALID_REPOS_ID if there
12614 is no 'copy-from' repository. */
12615 static svn_error_t *
12616 scan_addition(svn_wc__db_status_t *status,
12617 const char **op_root_relpath_p,
12618 const char **repos_relpath,
12619 apr_int64_t *repos_id,
12620 const char **original_repos_relpath,
12621 apr_int64_t *original_repos_id,
12622 svn_revnum_t *original_revision,
12623 const char **moved_from_relpath,
12624 const char **moved_from_op_root_relpath,
12625 int *moved_from_op_depth,
12626 svn_wc__db_wcroot_t *wcroot,
12627 const char *local_relpath,
12628 apr_pool_t *result_pool,
12629 apr_pool_t *scratch_pool)
12631 const char *op_root_relpath;
12632 const char *build_relpath = "";
12634 /* Initialize most of the OUT parameters. Generally, we'll only be filling
12635 in a subset of these, so it is easier to init all up front. Note that
12636 the STATUS parameter will be initialized once we read the status of
12637 the specified node. */
12638 if (op_root_relpath_p)
12639 *op_root_relpath_p = NULL;
12640 if (original_repos_relpath)
12641 *original_repos_relpath = NULL;
12642 if (original_repos_id)
12643 *original_repos_id = INVALID_REPOS_ID;
12644 if (original_revision)
12645 *original_revision = SVN_INVALID_REVNUM;
12646 if (moved_from_relpath)
12647 *moved_from_relpath = NULL;
12648 if (moved_from_op_root_relpath)
12649 *moved_from_op_root_relpath = NULL;
12650 if (moved_from_op_depth)
12651 *moved_from_op_depth = 0;
12654 svn_sqlite__stmt_t *stmt;
12655 svn_boolean_t have_row;
12656 svn_wc__db_status_t presence;
12658 const char *repos_prefix_path;
12660 /* ### is it faster to fetch fewer columns? */
12661 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12662 STMT_SELECT_WORKING_NODE));
12663 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
12664 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12668 /* Reset statement before returning */
12669 SVN_ERR(svn_sqlite__reset(stmt));
12671 /* ### maybe we should return a usage error instead? */
12672 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12673 _("The node '%s' was not found."),
12674 path_for_error_message(wcroot,
12679 presence = svn_sqlite__column_token(stmt, 1, presence_map);
12681 /* The starting node should exist normally. */
12682 op_depth = svn_sqlite__column_int(stmt, 0);
12683 if (op_depth == 0 || (presence != svn_wc__db_status_normal
12684 && presence != svn_wc__db_status_incomplete))
12685 /* reset the statement as part of the error generation process */
12686 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
12687 svn_sqlite__reset(stmt),
12688 _("Expected node '%s' to be added."),
12689 path_for_error_message(wcroot,
12693 if (original_revision)
12694 *original_revision = svn_sqlite__column_revnum(stmt, 12);
12696 /* Provide the default status; we'll override as appropriate. */
12699 if (presence == svn_wc__db_status_normal)
12700 *status = svn_wc__db_status_added;
12702 *status = svn_wc__db_status_incomplete;
12706 /* Calculate the op root local path components */
12707 op_root_relpath = svn_relpath_prefix(local_relpath, op_depth,
12709 repos_prefix_path = svn_relpath_skip_ancestor(op_root_relpath,
12712 if (op_root_relpath_p)
12713 *op_root_relpath_p = apr_pstrdup(result_pool, op_root_relpath);
12715 /* ### This if-statement is quite redundant.
12716 * ### We're checking all these values again within the body anyway.
12717 * ### The body should be broken up appropriately and move into the
12718 * ### outer scope. */
12719 if (original_repos_relpath
12720 || original_repos_id
12721 || (original_revision
12722 && *original_revision == SVN_INVALID_REVNUM)
12724 || moved_from_relpath || moved_from_op_root_relpath)
12726 if (local_relpath != op_root_relpath)
12727 /* requery to get the add/copy root */
12729 SVN_ERR(svn_sqlite__reset(stmt));
12731 SVN_ERR(svn_sqlite__bindf(stmt, "is",
12732 wcroot->wc_id, op_root_relpath));
12733 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12737 /* Reset statement before returning */
12738 SVN_ERR(svn_sqlite__reset(stmt));
12740 /* ### maybe we should return a usage error instead? */
12741 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12742 _("The node '%s' was not found."),
12743 path_for_error_message(wcroot,
12748 if (original_revision
12749 && *original_revision == SVN_INVALID_REVNUM)
12750 *original_revision = svn_sqlite__column_revnum(stmt, 12);
12753 if (original_repos_relpath)
12754 *original_repos_relpath = svn_sqlite__column_text(stmt, 11,
12757 if (!svn_sqlite__column_is_null(stmt, 10)
12759 || original_repos_id
12760 || moved_from_relpath || moved_from_op_root_relpath))
12761 /* If column 10 (original_repos_id) is NULL,
12762 this is a plain add, not a copy or a move */
12764 svn_boolean_t moved_here;
12765 if (original_repos_id)
12766 *original_repos_id = svn_sqlite__column_int64(stmt, 10);
12768 moved_here = svn_sqlite__column_boolean(stmt, 13 /* moved_here */);
12770 *status = moved_here ? svn_wc__db_status_moved_here
12771 : svn_wc__db_status_copied;
12774 && (moved_from_relpath || moved_from_op_root_relpath))
12778 err = get_moved_from_info(moved_from_relpath,
12779 moved_from_op_root_relpath,
12781 moved_from_op_depth,
12782 wcroot, local_relpath,
12787 return svn_error_compose_create(
12788 err, svn_sqlite__reset(stmt));
12794 /* ### This loop here is to skip up to the first node which is a BASE node,
12795 because base_get_info() doesn't accommodate the scenario that
12796 we're looking at here; we found the true op_root, which may be inside
12797 further changed trees. */
12798 if (repos_relpath || repos_id)
12800 const char *base_relpath;
12806 SVN_ERR(svn_sqlite__reset(stmt));
12808 /* Pointing at op_depth, look at the parent */
12809 repos_prefix_path =
12810 svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
12813 op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
12816 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, op_root_relpath));
12817 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12822 op_depth = svn_sqlite__column_int(stmt, 0);
12824 /* Skip to op_depth */
12825 tmp = op_root_relpath;
12827 op_root_relpath = svn_relpath_prefix(op_root_relpath, op_depth,
12829 repos_prefix_path = svn_relpath_join(
12830 svn_relpath_skip_ancestor(op_root_relpath, tmp),
12831 repos_prefix_path, scratch_pool);
12834 SVN_ERR(svn_sqlite__reset(stmt));
12836 build_relpath = repos_prefix_path;
12838 /* If we're here, then we have an added/copied/moved (start) node, and
12839 CURRENT_ABSPATH now points to a BASE node. Figure out the repository
12840 information for the current node, and use that to compute the start
12841 node's repository information. */
12842 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12843 &base_relpath, repos_id,
12844 NULL, NULL, NULL, NULL, NULL,
12845 NULL, NULL, NULL, NULL, NULL,
12846 wcroot, op_root_relpath,
12847 scratch_pool, scratch_pool));
12850 *repos_relpath = svn_relpath_join(base_relpath, build_relpath,
12854 SVN_ERR(svn_sqlite__reset(stmt));
12856 /* Postconditions */
12860 SVN_ERR_ASSERT(*status == svn_wc__db_status_added
12861 || *status == svn_wc__db_status_copied
12862 || *status == svn_wc__db_status_incomplete
12863 || *status == svn_wc__db_status_moved_here);
12864 if (*status == svn_wc__db_status_added)
12866 SVN_ERR_ASSERT(!original_repos_relpath
12867 || *original_repos_relpath == NULL);
12868 SVN_ERR_ASSERT(!original_revision
12869 || *original_revision == SVN_INVALID_REVNUM);
12870 SVN_ERR_ASSERT(!original_repos_id
12871 || *original_repos_id == INVALID_REPOS_ID);
12873 /* An upgrade with a missing directory can leave INCOMPLETE working
12874 op-roots. See upgrade_tests.py 29: upgrade with missing replaced dir
12876 else if (*status != svn_wc__db_status_incomplete)
12878 SVN_ERR_ASSERT(!original_repos_relpath
12879 || *original_repos_relpath != NULL);
12880 SVN_ERR_ASSERT(!original_revision
12881 || *original_revision != SVN_INVALID_REVNUM);
12882 SVN_ERR_ASSERT(!original_repos_id
12883 || *original_repos_id != INVALID_REPOS_ID);
12886 SVN_ERR_ASSERT(!op_root_relpath_p || *op_root_relpath_p != NULL);
12889 return SVN_NO_ERROR;
12893 svn_wc__db_scan_addition_internal(
12894 svn_wc__db_status_t *status,
12895 const char **op_root_relpath_p,
12896 const char **repos_relpath,
12897 apr_int64_t *repos_id,
12898 const char **original_repos_relpath,
12899 apr_int64_t *original_repos_id,
12900 svn_revnum_t *original_revision,
12901 svn_wc__db_wcroot_t *wcroot,
12902 const char *local_relpath,
12903 apr_pool_t *result_pool,
12904 apr_pool_t *scratch_pool)
12906 return svn_error_trace(
12907 scan_addition(status, op_root_relpath_p, repos_relpath, repos_id,
12908 original_repos_relpath, original_repos_id,
12909 original_revision, NULL, NULL, NULL,
12910 wcroot, local_relpath, result_pool, scratch_pool));
12914 svn_wc__db_scan_addition(svn_wc__db_status_t *status,
12915 const char **op_root_abspath,
12916 const char **repos_relpath,
12917 const char **repos_root_url,
12918 const char **repos_uuid,
12919 const char **original_repos_relpath,
12920 const char **original_root_url,
12921 const char **original_uuid,
12922 svn_revnum_t *original_revision,
12924 const char *local_abspath,
12925 apr_pool_t *result_pool,
12926 apr_pool_t *scratch_pool)
12928 svn_wc__db_wcroot_t *wcroot;
12929 const char *local_relpath;
12930 const char *op_root_relpath = NULL;
12931 apr_int64_t repos_id = INVALID_REPOS_ID;
12932 apr_int64_t original_repos_id = INVALID_REPOS_ID;
12933 apr_int64_t *repos_id_p
12934 = (repos_root_url || repos_uuid) ? &repos_id : NULL;
12935 apr_int64_t *original_repos_id_p
12936 = (original_root_url || original_uuid) ? &original_repos_id : NULL;
12938 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12940 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12941 local_abspath, scratch_pool, scratch_pool));
12942 VERIFY_USABLE_WCROOT(wcroot);
12944 SVN_WC__DB_WITH_TXN4(
12945 scan_addition(status,
12949 repos_relpath, repos_id_p,
12950 original_repos_relpath, original_repos_id_p,
12953 wcroot, local_relpath, result_pool, scratch_pool),
12954 svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot,
12955 repos_id, result_pool),
12956 svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
12957 wcroot, original_repos_id,
12962 if (op_root_abspath)
12963 *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
12965 /* REPOS_ID must be valid if requested; ORIGINAL_REPOS_ID need not be. */
12966 SVN_ERR_ASSERT(repos_id_p == NULL || repos_id != INVALID_REPOS_ID);
12968 return SVN_NO_ERROR;
12972 svn_wc__db_scan_moved(const char **moved_from_abspath,
12973 const char **op_root_abspath,
12974 const char **op_root_moved_from_abspath,
12975 const char **moved_from_delete_abspath,
12977 const char *local_abspath,
12978 apr_pool_t *result_pool,
12979 apr_pool_t *scratch_pool)
12981 svn_wc__db_wcroot_t *wcroot;
12982 const char *local_relpath;
12983 svn_wc__db_status_t status;
12984 const char *op_root_relpath = NULL;
12985 const char *moved_from_relpath = NULL;
12986 const char *moved_from_op_root_relpath = NULL;
12987 int moved_from_op_depth = -1;
12989 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12991 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12992 local_abspath, scratch_pool, scratch_pool));
12993 VERIFY_USABLE_WCROOT(wcroot);
12995 SVN_WC__DB_WITH_TXN(
12996 scan_addition(&status,
13003 ? &moved_from_relpath
13005 (op_root_moved_from_abspath
13006 || moved_from_delete_abspath)
13007 ? &moved_from_op_root_relpath
13009 moved_from_delete_abspath
13010 ? &moved_from_op_depth
13012 wcroot, local_relpath, scratch_pool, scratch_pool),
13015 if (status != svn_wc__db_status_moved_here || !moved_from_relpath)
13016 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
13017 _("Path '%s' was not moved here"),
13018 path_for_error_message(wcroot, local_relpath,
13021 if (op_root_abspath)
13022 *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
13025 if (moved_from_abspath)
13026 *moved_from_abspath = svn_dirent_join(wcroot->abspath, moved_from_relpath,
13029 if (op_root_moved_from_abspath)
13030 *op_root_moved_from_abspath = svn_dirent_join(wcroot->abspath,
13031 moved_from_op_root_relpath,
13034 /* The deleted node is either where we moved from, or one of its ancestors */
13035 if (moved_from_delete_abspath)
13037 const char *tmp = svn_relpath_prefix(moved_from_op_root_relpath,
13038 moved_from_op_depth, scratch_pool);
13040 *moved_from_delete_abspath = svn_dirent_join(wcroot->abspath, tmp,
13044 return SVN_NO_ERROR;
13047 /* ### Recursive helper for svn_wc__db_follow_moved_to()
13049 static svn_error_t *
13050 follow_moved_to(svn_wc__db_wcroot_t *wcroot,
13051 const char *local_relpath,
13053 apr_array_header_t **moved_tos,
13054 apr_pool_t *result_pool,
13055 apr_pool_t *scratch_pool)
13057 svn_sqlite__stmt_t *stmt;
13058 svn_boolean_t have_row;
13059 int shadowing_op_depth;
13060 const char *ancestor_relpath;
13061 const char *node_moved_to = NULL;
13064 /* Obtain the depth of the node directly shadowing local_relpath
13065 as it exists at OP_DEPTH, and perhaps moved to info */
13066 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13067 STMT_SELECT_OP_DEPTH_MOVED_TO));
13068 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
13070 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13073 shadowing_op_depth = svn_sqlite__column_int(stmt, 0);
13074 node_moved_to = svn_sqlite__column_text(stmt, 1, result_pool);
13078 struct svn_wc__db_moved_to_t *moved_to;
13080 moved_to = apr_palloc(result_pool, sizeof(*moved_to));
13081 moved_to->op_depth = shadowing_op_depth;
13082 moved_to->local_relpath = node_moved_to;
13083 APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
13087 SVN_ERR(svn_sqlite__reset(stmt));
13091 /* Node is not shadowed, so not moved */
13092 return SVN_NO_ERROR;
13094 else if (node_moved_to)
13096 /* Moved directly, so we have the final location */
13097 return SVN_NO_ERROR;
13099 /* Need to handle being moved via an ancestor. */
13100 ancestor_relpath = local_relpath;
13101 for (i = relpath_depth(local_relpath); i > shadowing_op_depth; --i)
13103 const char *ancestor_moved_to;
13105 ancestor_relpath = svn_relpath_dirname(ancestor_relpath, scratch_pool);
13107 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13108 STMT_SELECT_MOVED_TO));
13109 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, ancestor_relpath,
13110 shadowing_op_depth));
13111 SVN_ERR(svn_sqlite__step_row(stmt));
13113 ancestor_moved_to = svn_sqlite__column_text(stmt, 0, scratch_pool);
13114 SVN_ERR(svn_sqlite__reset(stmt));
13115 if (ancestor_moved_to)
13117 struct svn_wc__db_moved_to_t *moved_to;
13120 = svn_relpath_join(ancestor_moved_to,
13121 svn_relpath_skip_ancestor(ancestor_relpath,
13125 moved_to = apr_palloc(result_pool, sizeof(*moved_to));
13126 moved_to->op_depth = shadowing_op_depth;
13127 moved_to->local_relpath = node_moved_to;
13128 APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
13130 SVN_ERR(follow_moved_to(wcroot, node_moved_to,
13131 relpath_depth(ancestor_moved_to),
13132 moved_tos, result_pool, scratch_pool));
13138 return SVN_NO_ERROR;
13142 svn_wc__db_follow_moved_to(apr_array_header_t **moved_tos,
13144 const char *local_abspath,
13145 apr_pool_t *result_pool,
13146 apr_pool_t *scratch_pool)
13148 svn_wc__db_wcroot_t *wcroot;
13149 const char *local_relpath;
13151 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13153 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13154 local_abspath, scratch_pool, scratch_pool));
13155 VERIFY_USABLE_WCROOT(wcroot);
13157 *moved_tos = apr_array_make(result_pool, 0,
13158 sizeof(struct svn_wc__db_moved_to_t *));
13160 /* ### Wrap in a transaction */
13161 SVN_WC__DB_WITH_TXN(follow_moved_to(wcroot, local_relpath, 0, moved_tos,
13162 result_pool, scratch_pool),
13165 /* ### Convert moved_to to abspath */
13167 return SVN_NO_ERROR;
13171 svn_wc__db_scan_moved_to_internal(const char **move_src_relpath,
13172 const char **move_dst_relpath,
13173 const char **delete_relpath,
13174 svn_wc__db_wcroot_t *wcroot,
13175 const char *local_relpath,
13177 apr_pool_t *result_pool,
13178 apr_pool_t *scratch_pool)
13180 svn_sqlite__stmt_t *stmt;
13181 svn_boolean_t have_row;
13182 int delete_op_depth;
13183 const char *relpath = local_relpath;
13184 const char *dst_relpath;
13186 SVN_ERR_ASSERT(local_relpath[0]); /* Not valid on the WC root */
13188 if (move_src_relpath)
13189 *move_src_relpath = NULL;
13190 if (move_dst_relpath)
13191 *move_dst_relpath = NULL;
13192 if (delete_relpath)
13193 *delete_relpath = NULL;
13195 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13196 STMT_SELECT_OP_DEPTH_MOVED_TO));
13197 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath, op_depth));
13199 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13203 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
13204 svn_sqlite__reset(stmt),
13205 _("Node '%s' is not shadowed"),
13206 path_for_error_message(wcroot, local_relpath,
13210 delete_op_depth = svn_sqlite__column_int(stmt, 0);
13211 dst_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
13213 SVN_ERR(svn_sqlite__reset(stmt));
13215 while (!dst_relpath && have_row)
13217 relpath = svn_relpath_dirname(relpath, scratch_pool);
13219 if (relpath_depth(relpath) < delete_op_depth)
13222 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13223 STMT_SELECT_DEPTH_NODE));
13224 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath,
13227 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13230 dst_relpath = svn_sqlite__column_text(stmt, 13, scratch_pool);
13232 SVN_ERR(svn_sqlite__reset(stmt));
13237 if (move_src_relpath)
13238 *move_src_relpath = apr_pstrdup(result_pool, relpath);
13240 if (move_dst_relpath)
13241 *move_dst_relpath = apr_pstrdup(result_pool, dst_relpath);
13243 if (delete_relpath)
13244 *delete_relpath = svn_relpath_prefix(local_relpath, delete_op_depth,
13248 return SVN_NO_ERROR;
13251 /* Public (within libsvn_wc) absolute path version of
13252 svn_wc__db_op_depth_moved_to with the op-depth hard-coded to
13255 svn_wc__db_base_moved_to(const char **move_dst_abspath,
13256 const char **move_dst_op_root_abspath,
13257 const char **move_src_root_abspath,
13258 const char **delete_abspath,
13260 const char *local_abspath,
13261 apr_pool_t *result_pool,
13262 apr_pool_t *scratch_pool)
13264 svn_wc__db_wcroot_t *wcroot;
13265 const char *local_relpath;
13266 const char *dst_root_relpath;
13267 const char *src_root_relpath, *delete_relpath;
13269 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13272 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13273 local_abspath, scratch_pool, scratch_pool));
13274 VERIFY_USABLE_WCROOT(wcroot);
13276 SVN_WC__DB_WITH_TXN(svn_wc__db_scan_moved_to_internal(&src_root_relpath,
13279 wcroot, local_relpath,
13285 if (move_dst_abspath)
13286 *move_dst_abspath =
13288 ? svn_dirent_join(wcroot->abspath,
13291 svn_relpath_skip_ancestor(src_root_relpath,
13297 if (move_dst_op_root_abspath)
13298 *move_dst_op_root_abspath =
13300 ? svn_dirent_join(wcroot->abspath, dst_root_relpath, result_pool)
13303 if (move_src_root_abspath)
13304 *move_src_root_abspath =
13306 ? svn_dirent_join(wcroot->abspath, src_root_relpath, result_pool)
13309 if (delete_abspath)
13312 ? svn_dirent_join(wcroot->abspath, delete_relpath, result_pool)
13315 return SVN_NO_ERROR;
13319 svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb,
13320 apr_int64_t *repos_id,
13321 apr_int64_t *wc_id,
13322 svn_wc__db_t *wc_db,
13323 const char *dir_abspath,
13324 const char *repos_root_url,
13325 const char *repos_uuid,
13326 apr_pool_t *scratch_pool)
13328 svn_wc__db_wcroot_t *wcroot;
13330 /* Upgrade is inherently exclusive so specify exclusive locking. */
13331 SVN_ERR(create_db(sdb, repos_id, wc_id, dir_abspath,
13332 repos_root_url, repos_uuid,
13334 NULL, SVN_INVALID_REVNUM, svn_depth_unknown,
13335 TRUE /* exclusive */,
13337 wc_db->state_pool, scratch_pool));
13339 SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
13340 apr_pstrdup(wc_db->state_pool,
13342 *sdb, *wc_id, FORMAT_FROM_SDB,
13343 FALSE /* auto-upgrade */,
13344 wc_db->state_pool, scratch_pool));
13346 /* The WCROOT is complete. Stash it into DB. */
13347 svn_hash_sets(wc_db->dir_data, wcroot->abspath, wcroot);
13349 return SVN_NO_ERROR;
13353 svn_wc__db_upgrade_insert_external(svn_wc__db_t *db,
13354 const char *local_abspath,
13355 svn_node_kind_t kind,
13356 const char *parent_abspath,
13357 const char *def_local_abspath,
13358 const char *repos_relpath,
13359 const char *repos_root_url,
13360 const char *repos_uuid,
13361 svn_revnum_t def_peg_revision,
13362 svn_revnum_t def_revision,
13363 apr_pool_t *scratch_pool)
13365 svn_wc__db_wcroot_t *wcroot;
13366 const char *def_local_relpath;
13367 svn_sqlite__stmt_t *stmt;
13368 svn_boolean_t have_row;
13369 apr_int64_t repos_id;
13371 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13373 /* We know only of DEF_LOCAL_ABSPATH that it definitely belongs to "this"
13374 * WC, i.e. where the svn:externals prop is set. The external target path
13375 * itself may be "hidden behind" other working copies. */
13376 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &def_local_relpath,
13377 db, def_local_abspath,
13378 scratch_pool, scratch_pool));
13379 VERIFY_USABLE_WCROOT(wcroot);
13382 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13383 STMT_SELECT_REPOSITORY));
13384 SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
13385 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13388 repos_id = svn_sqlite__column_int64(stmt, 0);
13389 SVN_ERR(svn_sqlite__reset(stmt));
13393 /* Need to set up a new repository row. */
13394 SVN_ERR(create_repos_id(&repos_id, repos_root_url, repos_uuid,
13395 wcroot->sdb, scratch_pool));
13398 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13399 STMT_INSERT_EXTERNAL));
13401 /* wc_id, local_relpath, parent_relpath, presence, kind, def_local_relpath,
13402 * repos_id, def_repos_relpath, def_operational_revision, def_revision */
13403 SVN_ERR(svn_sqlite__bindf(stmt, "issstsis",
13405 svn_dirent_skip_ancestor(wcroot->abspath,
13407 svn_dirent_skip_ancestor(wcroot->abspath,
13415 if (SVN_IS_VALID_REVNUM(def_peg_revision))
13416 SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, def_peg_revision));
13418 if (SVN_IS_VALID_REVNUM(def_revision))
13419 SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, def_revision));
13421 SVN_ERR(svn_sqlite__insert(NULL, stmt));
13423 return SVN_NO_ERROR;
13427 svn_wc__db_wq_add_internal(svn_wc__db_wcroot_t *wcroot,
13428 const svn_skel_t *work_item,
13429 apr_pool_t *scratch_pool)
13431 /* Add the work item(s) to the WORK_QUEUE. */
13432 return svn_error_trace(add_work_items(wcroot->sdb, work_item,
13437 svn_wc__db_wq_add(svn_wc__db_t *db,
13438 const char *wri_abspath,
13439 const svn_skel_t *work_item,
13440 apr_pool_t *scratch_pool)
13442 svn_wc__db_wcroot_t *wcroot;
13443 const char *local_relpath;
13445 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13447 /* Quick exit, if there are no work items to queue up. */
13448 if (work_item == NULL)
13449 return SVN_NO_ERROR;
13451 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13452 wri_abspath, scratch_pool, scratch_pool));
13453 VERIFY_USABLE_WCROOT(wcroot);
13455 /* Add the work item(s) to the WORK_QUEUE. */
13456 return svn_error_trace(add_work_items(wcroot->sdb, work_item,
13460 /* The body of svn_wc__db_wq_fetch_next().
13462 static svn_error_t *
13463 wq_fetch_next(apr_uint64_t *id,
13464 svn_skel_t **work_item,
13465 svn_wc__db_wcroot_t *wcroot,
13466 const char *local_relpath,
13467 apr_uint64_t completed_id,
13468 apr_pool_t *result_pool,
13469 apr_pool_t *scratch_pool)
13471 svn_sqlite__stmt_t *stmt;
13472 svn_boolean_t have_row;
13474 if (completed_id != 0)
13476 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13477 STMT_DELETE_WORK_ITEM));
13478 SVN_ERR(svn_sqlite__bind_int64(stmt, 1, completed_id));
13480 SVN_ERR(svn_sqlite__step_done(stmt));
13483 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13484 STMT_SELECT_WORK_ITEM));
13485 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13497 *id = svn_sqlite__column_int64(stmt, 0);
13499 val = svn_sqlite__column_blob(stmt, 1, &len, result_pool);
13501 *work_item = svn_skel__parse(val, len, result_pool);
13504 return svn_error_trace(svn_sqlite__reset(stmt));
13508 svn_wc__db_wq_fetch_next(apr_uint64_t *id,
13509 svn_skel_t **work_item,
13511 const char *wri_abspath,
13512 apr_uint64_t completed_id,
13513 apr_pool_t *result_pool,
13514 apr_pool_t *scratch_pool)
13516 svn_wc__db_wcroot_t *wcroot;
13517 const char *local_relpath;
13519 SVN_ERR_ASSERT(id != NULL);
13520 SVN_ERR_ASSERT(work_item != NULL);
13521 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13523 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13524 wri_abspath, scratch_pool, scratch_pool));
13525 VERIFY_USABLE_WCROOT(wcroot);
13527 SVN_WC__DB_WITH_TXN(
13528 wq_fetch_next(id, work_item,
13529 wcroot, local_relpath, completed_id,
13530 result_pool, scratch_pool),
13533 return SVN_NO_ERROR;
13536 /* Records timestamp and date for one or more files in wcroot */
13537 static svn_error_t *
13538 wq_record(svn_wc__db_wcroot_t *wcroot,
13539 apr_hash_t *record_map,
13540 apr_pool_t *scratch_pool)
13542 apr_hash_index_t *hi;
13543 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
13545 for (hi = apr_hash_first(scratch_pool, record_map); hi;
13546 hi = apr_hash_next(hi))
13548 const char *local_abspath = apr_hash_this_key(hi);
13549 const svn_io_dirent2_t *dirent = apr_hash_this_val(hi);
13550 const char *local_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
13553 svn_pool_clear(iterpool);
13555 if (! local_relpath)
13558 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
13559 dirent->filesize, dirent->mtime,
13563 svn_pool_destroy(iterpool);
13564 return SVN_NO_ERROR;
13568 svn_wc__db_wq_record_and_fetch_next(apr_uint64_t *id,
13569 svn_skel_t **work_item,
13571 const char *wri_abspath,
13572 apr_uint64_t completed_id,
13573 apr_hash_t *record_map,
13574 apr_pool_t *result_pool,
13575 apr_pool_t *scratch_pool)
13577 svn_wc__db_wcroot_t *wcroot;
13578 const char *local_relpath;
13580 SVN_ERR_ASSERT(id != NULL);
13581 SVN_ERR_ASSERT(work_item != NULL);
13582 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13584 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13585 wri_abspath, scratch_pool, scratch_pool));
13586 VERIFY_USABLE_WCROOT(wcroot);
13588 SVN_WC__DB_WITH_TXN(
13589 svn_error_compose_create(
13590 wq_fetch_next(id, work_item,
13591 wcroot, local_relpath, completed_id,
13592 result_pool, scratch_pool),
13593 wq_record(wcroot, record_map, scratch_pool)),
13596 return SVN_NO_ERROR;
13601 /* ### temporary API. remove before release. */
13603 svn_wc__db_temp_get_format(int *format,
13605 const char *local_dir_abspath,
13606 apr_pool_t *scratch_pool)
13608 svn_wc__db_wcroot_t *wcroot;
13609 const char *local_relpath;
13612 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13613 /* ### assert that we were passed a directory? */
13615 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13616 local_dir_abspath, scratch_pool, scratch_pool);
13618 /* If we hit an error examining this directory, then declare this
13619 directory to not be a working copy. */
13622 if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
13623 return svn_error_trace(err);
13624 svn_error_clear(err);
13626 /* Remap the returned error. */
13628 return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
13629 _("'%s' is not a working copy"),
13630 svn_dirent_local_style(local_dir_abspath,
13634 SVN_ERR_ASSERT(wcroot != NULL);
13635 SVN_ERR_ASSERT(wcroot->format >= 1);
13637 *format = wcroot->format;
13639 return SVN_NO_ERROR;
13642 /* ### temporary API. remove before release. */
13643 svn_wc_adm_access_t *
13644 svn_wc__db_temp_get_access(svn_wc__db_t *db,
13645 const char *local_dir_abspath,
13646 apr_pool_t *scratch_pool)
13648 const char *local_relpath;
13649 svn_wc__db_wcroot_t *wcroot;
13652 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13654 /* ### we really need to assert that we were passed a directory. sometimes
13655 ### adm_retrieve_internal is asked about a file, and then it asks us
13656 ### for an access baton for it. we should definitely return NULL, but
13657 ### ideally: the caller would never ask us about a non-directory. */
13659 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13660 db, local_dir_abspath, scratch_pool, scratch_pool);
13663 svn_error_clear(err);
13670 return svn_hash_gets(wcroot->access_cache, local_dir_abspath);
13674 /* ### temporary API. remove before release. */
13676 svn_wc__db_temp_set_access(svn_wc__db_t *db,
13677 const char *local_dir_abspath,
13678 svn_wc_adm_access_t *adm_access,
13679 apr_pool_t *scratch_pool)
13681 const char *local_relpath;
13682 svn_wc__db_wcroot_t *wcroot;
13685 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13686 /* ### assert that we were passed a directory? */
13688 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13689 db, local_dir_abspath, scratch_pool, scratch_pool);
13692 /* We don't even have a wcroot, so just bail. */
13693 svn_error_clear(err);
13697 /* Better not override something already there. */
13698 SVN_ERR_ASSERT_NO_RETURN(
13699 svn_hash_gets(wcroot->access_cache, local_dir_abspath) == NULL
13701 svn_hash_sets(wcroot->access_cache, local_dir_abspath, adm_access);
13705 /* ### temporary API. remove before release. */
13707 svn_wc__db_temp_close_access(svn_wc__db_t *db,
13708 const char *local_dir_abspath,
13709 svn_wc_adm_access_t *adm_access,
13710 apr_pool_t *scratch_pool)
13712 const char *local_relpath;
13713 svn_wc__db_wcroot_t *wcroot;
13715 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13716 /* ### assert that we were passed a directory? */
13718 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13719 local_dir_abspath, scratch_pool, scratch_pool));
13720 svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13722 return SVN_NO_ERROR;
13726 /* ### temporary API. remove before release. */
13728 svn_wc__db_temp_clear_access(svn_wc__db_t *db,
13729 const char *local_dir_abspath,
13730 apr_pool_t *scratch_pool)
13732 const char *local_relpath;
13733 svn_wc__db_wcroot_t *wcroot;
13736 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13737 /* ### assert that we were passed a directory? */
13739 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13740 db, local_dir_abspath, scratch_pool, scratch_pool);
13743 svn_error_clear(err);
13747 svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13752 svn_wc__db_temp_get_all_access(svn_wc__db_t *db,
13753 apr_pool_t *result_pool)
13755 apr_hash_t *result = apr_hash_make(result_pool);
13756 apr_hash_index_t *hi;
13758 for (hi = apr_hash_first(result_pool, db->dir_data);
13760 hi = apr_hash_next(hi))
13762 const svn_wc__db_wcroot_t *wcroot = apr_hash_this_val(hi);
13764 /* This is highly redundant, 'cause the same WCROOT will appear many
13765 times in dir_data. */
13766 result = apr_hash_overlay(result_pool, result, wcroot->access_cache);
13774 svn_wc__db_temp_borrow_sdb(svn_sqlite__db_t **sdb,
13776 const char *local_dir_abspath,
13777 apr_pool_t *scratch_pool)
13779 svn_wc__db_wcroot_t *wcroot;
13780 const char *local_relpath;
13782 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13784 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13785 local_dir_abspath, scratch_pool, scratch_pool));
13786 VERIFY_USABLE_WCROOT(wcroot);
13788 *sdb = wcroot->sdb;
13790 return SVN_NO_ERROR;
13795 svn_wc__db_read_conflict_victims(const apr_array_header_t **victims,
13797 const char *local_abspath,
13798 apr_pool_t *result_pool,
13799 apr_pool_t *scratch_pool)
13801 svn_wc__db_wcroot_t *wcroot;
13802 const char *local_relpath;
13803 svn_sqlite__stmt_t *stmt;
13804 svn_boolean_t have_row;
13805 apr_array_header_t *new_victims;
13807 /* The parent should be a working copy directory. */
13808 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13809 local_abspath, scratch_pool, scratch_pool));
13810 VERIFY_USABLE_WCROOT(wcroot);
13812 /* ### This will be much easier once we have all conflicts in one
13815 /* Look for text, tree and property conflicts in ACTUAL */
13816 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13817 STMT_SELECT_CONFLICT_VICTIMS));
13818 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13820 new_victims = apr_array_make(result_pool, 0, sizeof(const char *));
13822 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13825 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
13827 APR_ARRAY_PUSH(new_victims, const char *) =
13828 svn_relpath_basename(child_relpath, result_pool);
13830 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13833 SVN_ERR(svn_sqlite__reset(stmt));
13835 *victims = new_victims;
13836 return SVN_NO_ERROR;
13839 /* The body of svn_wc__db_get_conflict_marker_files().
13841 static svn_error_t *
13842 get_conflict_marker_files(apr_hash_t **marker_files_p,
13843 svn_wc__db_wcroot_t *wcroot,
13844 const char *local_relpath,
13846 apr_pool_t *result_pool,
13847 apr_pool_t *scratch_pool)
13849 svn_sqlite__stmt_t *stmt;
13850 svn_boolean_t have_row;
13851 apr_hash_t *marker_files = apr_hash_make(result_pool);
13853 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13854 STMT_SELECT_ACTUAL_NODE));
13855 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13856 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13858 if (have_row && !svn_sqlite__column_is_null(stmt, 2))
13861 const void *data = svn_sqlite__column_blob(stmt, 2, &len, NULL);
13862 svn_skel_t *conflicts;
13863 const apr_array_header_t *markers;
13866 conflicts = svn_skel__parse(data, len, scratch_pool);
13868 /* ### ADD markers to *marker_files */
13869 SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13871 result_pool, scratch_pool));
13873 for (i = 0; markers && (i < markers->nelts); i++)
13875 const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13877 svn_hash_sets(marker_files, marker_abspath, "");
13880 SVN_ERR(svn_sqlite__reset(stmt));
13882 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13883 STMT_SELECT_CONFLICT_VICTIMS));
13884 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13885 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13890 const void *data = svn_sqlite__column_blob(stmt, 1, &len, NULL);
13892 const apr_array_header_t *markers;
13897 svn_skel_t *conflicts;
13898 conflicts = svn_skel__parse(data, len, scratch_pool);
13900 SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13902 result_pool, scratch_pool));
13904 for (i = 0; markers && (i < markers->nelts); i++)
13906 const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13908 svn_hash_sets(marker_files, marker_abspath, "");
13912 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13915 if (apr_hash_count(marker_files))
13916 *marker_files_p = marker_files;
13918 *marker_files_p = NULL;
13920 return svn_error_trace(svn_sqlite__reset(stmt));
13924 svn_wc__db_get_conflict_marker_files(apr_hash_t **marker_files,
13926 const char *local_abspath,
13927 apr_pool_t *result_pool,
13928 apr_pool_t *scratch_pool)
13930 svn_wc__db_wcroot_t *wcroot;
13931 const char *local_relpath;
13933 /* The parent should be a working copy directory. */
13934 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13935 local_abspath, scratch_pool, scratch_pool));
13936 VERIFY_USABLE_WCROOT(wcroot);
13938 SVN_WC__DB_WITH_TXN(
13939 get_conflict_marker_files(marker_files, wcroot, local_relpath, db,
13940 result_pool, scratch_pool),
13943 return SVN_NO_ERROR;
13948 svn_wc__db_read_conflict(svn_skel_t **conflict,
13949 svn_node_kind_t *kind,
13950 apr_hash_t **props,
13952 const char *local_abspath,
13953 apr_pool_t *result_pool,
13954 apr_pool_t *scratch_pool)
13956 svn_wc__db_wcroot_t *wcroot;
13957 const char *local_relpath;
13959 /* The parent should be a working copy directory. */
13960 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13961 local_abspath, scratch_pool, scratch_pool));
13962 VERIFY_USABLE_WCROOT(wcroot);
13964 return svn_error_trace(svn_wc__db_read_conflict_internal(conflict, kind, props,
13965 wcroot, local_relpath,
13971 svn_wc__db_read_conflict_internal(svn_skel_t **conflict,
13972 svn_node_kind_t *kind,
13973 apr_hash_t **props,
13974 svn_wc__db_wcroot_t *wcroot,
13975 const char *local_relpath,
13976 apr_pool_t *result_pool,
13977 apr_pool_t *scratch_pool)
13979 svn_sqlite__stmt_t *stmt;
13980 svn_boolean_t have_row;
13983 *kind = svn_node_none;
13987 /* Check if we have a conflict in ACTUAL */
13988 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13989 STMT_SELECT_ACTUAL_NODE));
13990 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13992 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13996 apr_size_t cfl_len;
13997 const void *cfl_data;
13999 /* svn_skel__parse doesn't copy data, so store in result_pool */
14000 cfl_data = svn_sqlite__column_blob(stmt, 2, &cfl_len, result_pool);
14003 *conflict = svn_skel__parse(cfl_data, cfl_len, result_pool);
14011 err = svn_error_trace(svn_sqlite__column_properties(props, stmt, 1,
14016 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
14022 SVN_ERR(svn_sqlite__reset(stmt));
14024 if (!have_row || kind || (props && !*props))
14026 svn_error_t *err = NULL;
14027 svn_boolean_t have_info = FALSE;
14029 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14030 STMT_SELECT_NODE_INFO));
14032 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
14035 SVN_ERR(svn_sqlite__step(&have_info, stmt));
14041 svn_wc__db_status_t status;
14042 int op_depth = svn_sqlite__column_int(stmt, 0);
14044 status = svn_sqlite__column_token(stmt, 3, presence_map);
14047 err = convert_to_working_status(&status, status);
14049 if (!err && (status == svn_wc__db_status_normal
14050 || status == svn_wc__db_status_added
14051 || status == svn_wc__db_status_deleted
14052 || status == svn_wc__db_status_incomplete))
14054 *kind = svn_sqlite__column_token(stmt, 4, kind_map);
14058 /* Need props, and no props in ACTUAL? */
14059 if (!err && (props && !*props))
14061 err = svn_sqlite__column_properties(props, stmt, 14,
14062 result_pool, scratch_pool);
14066 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14068 if (!have_row && !have_info)
14070 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14071 _("The node '%s' was not found."),
14072 path_for_error_message(wcroot,
14078 return SVN_NO_ERROR;
14083 svn_wc__db_read_kind(svn_node_kind_t *kind,
14085 const char *local_abspath,
14086 svn_boolean_t allow_missing,
14087 svn_boolean_t show_deleted,
14088 svn_boolean_t show_hidden,
14089 apr_pool_t *scratch_pool)
14091 svn_wc__db_wcroot_t *wcroot;
14092 const char *local_relpath;
14093 svn_sqlite__stmt_t *stmt_info;
14094 svn_boolean_t have_info;
14095 svn_wc__db_status_t status;
14097 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14099 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14100 local_abspath, scratch_pool, scratch_pool));
14101 VERIFY_USABLE_WCROOT(wcroot);
14103 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
14104 STMT_SELECT_NODE_INFO));
14105 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
14106 SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
14112 *kind = svn_node_unknown;
14113 SVN_ERR(svn_sqlite__reset(stmt_info));
14114 return SVN_NO_ERROR;
14118 SVN_ERR(svn_sqlite__reset(stmt_info));
14119 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14120 _("The node '%s' was not found."),
14121 path_for_error_message(wcroot,
14127 status = svn_sqlite__column_token(stmt_info, 3, presence_map);
14129 if (show_deleted && status == svn_wc__db_status_base_deleted)
14131 /* Let's return the kind of what is really deleted insead of what
14132 we have cached in the base-deleted record */
14134 SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
14138 /* No lower layer deleted? Database inconsistency! */
14139 *kind = svn_node_none;
14140 return svn_error_trace(svn_sqlite__reset(stmt_info));
14144 if (!(show_deleted && show_hidden))
14146 int op_depth = svn_sqlite__column_int(stmt_info, 0);
14147 svn_boolean_t report_none = FALSE;
14150 SVN_ERR(convert_to_working_status(&status, status));
14154 case svn_wc__db_status_not_present:
14155 if (! (show_hidden && show_deleted))
14156 report_none = TRUE;
14158 case svn_wc__db_status_excluded:
14159 case svn_wc__db_status_server_excluded:
14161 report_none = TRUE;
14163 case svn_wc__db_status_deleted:
14164 if (! show_deleted)
14165 report_none = TRUE;
14173 *kind = svn_node_none;
14174 return svn_error_trace(svn_sqlite__reset(stmt_info));
14178 *kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
14180 return svn_error_trace(svn_sqlite__reset(stmt_info));
14184 svn_wc__db_is_wcroot(svn_boolean_t *is_wcroot,
14186 const char *local_abspath,
14187 apr_pool_t *scratch_pool)
14189 svn_wc__db_wcroot_t *wcroot;
14190 const char *local_relpath;
14192 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14194 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14195 local_abspath, scratch_pool, scratch_pool));
14196 VERIFY_USABLE_WCROOT(wcroot);
14198 if (*local_relpath != '\0')
14200 *is_wcroot = FALSE; /* Node is a file, or has a parent directory within
14202 return SVN_NO_ERROR;
14207 return SVN_NO_ERROR;
14210 /* Find a node's kind and whether it is switched, putting the outputs in
14211 * *IS_SWITCHED and *KIND. Either of the outputs may be NULL if not wanted.
14213 static svn_error_t *
14214 db_is_switched(svn_boolean_t *is_switched,
14215 svn_node_kind_t *kind,
14216 svn_wc__db_wcroot_t *wcroot,
14217 const char *local_relpath,
14218 apr_pool_t *scratch_pool)
14220 svn_wc__db_status_t status;
14221 apr_int64_t repos_id;
14222 const char *repos_relpath;
14224 const char *parent_local_relpath;
14225 apr_int64_t parent_repos_id;
14226 const char *parent_repos_relpath;
14228 SVN_ERR_ASSERT(*local_relpath != '\0'); /* Handled in wrapper */
14230 SVN_ERR(read_info(&status, kind, NULL, &repos_relpath, &repos_id, NULL,
14231 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
14232 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
14233 wcroot, local_relpath, scratch_pool, scratch_pool));
14235 if (status == svn_wc__db_status_server_excluded
14236 || status == svn_wc__db_status_excluded
14237 || status == svn_wc__db_status_not_present)
14239 return svn_error_createf(
14240 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14241 _("The node '%s' was not found."),
14242 path_for_error_message(wcroot, local_relpath,
14245 else if (! repos_relpath)
14247 /* Node is shadowed; easy out */
14249 *is_switched = FALSE;
14251 return SVN_NO_ERROR;
14255 return SVN_NO_ERROR;
14257 svn_relpath_split(&parent_local_relpath, &name, local_relpath, scratch_pool);
14259 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
14260 &parent_repos_relpath,
14261 &parent_repos_id, NULL, NULL, NULL,
14262 NULL, NULL, NULL, NULL, NULL,
14264 wcroot, parent_local_relpath,
14265 scratch_pool, scratch_pool));
14267 if (repos_id != parent_repos_id)
14268 *is_switched = TRUE;
14271 const char *expected_relpath;
14273 expected_relpath = svn_relpath_join(parent_repos_relpath, name,
14276 *is_switched = (strcmp(expected_relpath, repos_relpath) != 0);
14279 return SVN_NO_ERROR;
14283 svn_wc__db_is_switched(svn_boolean_t *is_wcroot,
14284 svn_boolean_t *is_switched,
14285 svn_node_kind_t *kind,
14287 const char *local_abspath,
14288 apr_pool_t *scratch_pool)
14290 svn_wc__db_wcroot_t *wcroot;
14291 const char *local_relpath;
14293 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14295 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14296 local_abspath, scratch_pool, scratch_pool));
14297 VERIFY_USABLE_WCROOT(wcroot);
14300 *is_switched = FALSE;
14302 if (*local_relpath == '\0')
14309 *kind = svn_node_dir;
14310 return SVN_NO_ERROR;
14314 *is_wcroot = FALSE;
14316 if (! is_switched && ! kind)
14317 return SVN_NO_ERROR;
14319 SVN_WC__DB_WITH_TXN(
14320 db_is_switched(is_switched, kind, wcroot, local_relpath, scratch_pool),
14322 return SVN_NO_ERROR;
14327 svn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath,
14329 const char *wri_abspath,
14330 apr_pool_t *result_pool,
14331 apr_pool_t *scratch_pool)
14333 svn_wc__db_wcroot_t *wcroot;
14334 const char *local_relpath;
14336 SVN_ERR_ASSERT(temp_dir_abspath != NULL);
14337 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
14339 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14340 wri_abspath, scratch_pool, scratch_pool));
14341 VERIFY_USABLE_WCROOT(wcroot);
14343 *temp_dir_abspath = svn_dirent_join_many(result_pool,
14345 svn_wc_get_adm_dir(scratch_pool),
14346 WCROOT_TEMPDIR_RELPATH,
14348 return SVN_NO_ERROR;
14352 /* Helper for wclock_obtain_cb() to steal an existing lock */
14353 static svn_error_t *
14354 wclock_steal(svn_wc__db_wcroot_t *wcroot,
14355 const char *local_relpath,
14356 apr_pool_t *scratch_pool)
14358 svn_sqlite__stmt_t *stmt;
14360 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_WC_LOCK));
14361 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14363 SVN_ERR(svn_sqlite__step_done(stmt));
14365 return SVN_NO_ERROR;
14369 /* The body of svn_wc__db_wclock_obtain().
14371 static svn_error_t *
14372 wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot,
14373 const char *local_relpath,
14374 int levels_to_lock,
14375 svn_boolean_t steal_lock,
14376 svn_boolean_t enforce_empty_wq,
14377 apr_pool_t *scratch_pool)
14379 svn_sqlite__stmt_t *stmt;
14381 const char *lock_relpath;
14384 svn_boolean_t got_row;
14386 svn_wc__db_wclock_t lock;
14388 /* Upgrade locks the root before the node exists. Apart from that
14389 the root node always exists so we will just skip the check.
14391 ### Perhaps the lock for upgrade should be created when the db is
14392 created? 1.6 used to lock .svn on creation. */
14393 if (local_relpath[0])
14395 svn_boolean_t exists;
14397 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
14399 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14400 _("The node '%s' was not found."),
14401 path_for_error_message(wcroot,
14406 if (enforce_empty_wq)
14407 SVN_ERR(svn_wc__db_verify_no_work(wcroot->sdb));
14409 /* Check if there are nodes locked below the new lock root */
14410 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_FIND_WC_LOCK));
14411 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14413 lock_depth = relpath_depth(local_relpath);
14414 max_depth = lock_depth + levels_to_lock;
14416 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14420 svn_boolean_t own_lock;
14422 lock_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
14424 /* If we are not locking with depth infinity, check if this lock
14425 voids our lock request */
14426 if (levels_to_lock >= 0
14427 && relpath_depth(lock_relpath) > max_depth)
14429 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14433 /* Check if we are the lock owner, because we should be able to
14434 extend our lock. */
14435 err = svn_wc__db_wclock_owns_lock_internal(&own_lock, wcroot,
14437 TRUE, scratch_pool);
14440 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14442 if (!own_lock && !steal_lock)
14444 SVN_ERR(svn_sqlite__reset(stmt));
14445 err = svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
14446 _("'%s' is already locked."),
14447 path_for_error_message(wcroot,
14450 return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14451 _("Working copy '%s' locked."),
14452 path_for_error_message(wcroot,
14456 else if (!own_lock)
14458 err = wclock_steal(wcroot, lock_relpath, scratch_pool);
14461 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14464 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14467 SVN_ERR(svn_sqlite__reset(stmt));
14470 SVN_ERR(wclock_steal(wcroot, local_relpath, scratch_pool));
14472 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_WC_LOCK));
14473 lock_relpath = local_relpath;
14477 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, lock_relpath));
14479 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14483 int levels = svn_sqlite__column_int(stmt, 0);
14485 levels += relpath_depth(lock_relpath);
14487 SVN_ERR(svn_sqlite__reset(stmt));
14489 if (levels == -1 || levels >= lock_depth)
14492 err = svn_error_createf(
14493 SVN_ERR_WC_LOCKED, NULL,
14494 _("'%s' is already locked."),
14495 svn_dirent_local_style(
14496 svn_dirent_join(wcroot->abspath,
14500 return svn_error_createf(
14501 SVN_ERR_WC_LOCKED, err,
14502 _("Working copy '%s' locked."),
14503 path_for_error_message(wcroot,
14508 break; /* There can't be interesting locks on higher nodes */
14511 SVN_ERR(svn_sqlite__reset(stmt));
14513 if (!*lock_relpath)
14516 lock_relpath = svn_relpath_dirname(lock_relpath, scratch_pool);
14519 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_WC_LOCK));
14520 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14522 err = svn_sqlite__insert(NULL, stmt);
14524 return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14525 _("Failed to lock working copy '%s'."),
14526 path_for_error_message(wcroot,
14530 /* And finally store that we obtained the lock */
14531 lock.local_relpath = apr_pstrdup(wcroot->owned_locks->pool, local_relpath);
14532 lock.levels = levels_to_lock;
14533 APR_ARRAY_PUSH(wcroot->owned_locks, svn_wc__db_wclock_t) = lock;
14535 return SVN_NO_ERROR;
14540 svn_wc__db_wclock_obtain(svn_wc__db_t *db,
14541 const char *local_abspath,
14542 int levels_to_lock,
14543 svn_boolean_t steal_lock,
14544 apr_pool_t *scratch_pool)
14546 svn_wc__db_wcroot_t *wcroot;
14547 const char *local_relpath;
14549 SVN_ERR_ASSERT(levels_to_lock >= -1);
14550 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14552 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14554 scratch_pool, scratch_pool));
14555 VERIFY_USABLE_WCROOT(wcroot);
14560 int depth = relpath_depth(local_relpath);
14562 for (i = 0; i < wcroot->owned_locks->nelts; i++)
14564 svn_wc__db_wclock_t* lock = &APR_ARRAY_IDX(wcroot->owned_locks,
14565 i, svn_wc__db_wclock_t);
14567 if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14568 && (lock->levels == -1
14569 || (lock->levels + relpath_depth(lock->local_relpath))
14572 return svn_error_createf(
14573 SVN_ERR_WC_LOCKED, NULL,
14574 _("'%s' is already locked via '%s'."),
14575 svn_dirent_local_style(local_abspath, scratch_pool),
14576 path_for_error_message(wcroot, lock->local_relpath,
14582 SVN_WC__DB_WITH_TXN(
14583 wclock_obtain_cb(wcroot, local_relpath, levels_to_lock, steal_lock,
14584 db->enforce_empty_wq, scratch_pool),
14586 return SVN_NO_ERROR;
14590 /* The body of svn_wc__db_wclock_find_root() and svn_wc__db_wclocked(). */
14591 static svn_error_t *
14592 find_wclock(const char **lock_relpath,
14593 svn_wc__db_wcroot_t *wcroot,
14594 const char *dir_relpath,
14595 apr_pool_t *result_pool,
14596 apr_pool_t *scratch_pool)
14598 svn_sqlite__stmt_t *stmt;
14599 svn_boolean_t have_row;
14600 int dir_depth = relpath_depth(dir_relpath);
14601 const char *first_relpath;
14603 /* Check for locks on all directories that might be ancestors.
14604 As our new apis only use recursive locks the number of locks stored
14605 in the DB will be very low */
14606 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14607 STMT_SELECT_ANCESTOR_WCLOCKS));
14609 /* Get the top level relpath to reduce the worst case number of results
14610 to the number of directories below this node plus two.
14611 (1: the node itself and 2: the wcroot). */
14612 first_relpath = strchr(dir_relpath, '/');
14614 if (first_relpath != NULL)
14615 first_relpath = apr_pstrndup(scratch_pool, dir_relpath,
14616 first_relpath - dir_relpath);
14618 first_relpath = dir_relpath;
14620 SVN_ERR(svn_sqlite__bindf(stmt, "iss",
14625 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14629 const char *relpath = svn_sqlite__column_text(stmt, 0, NULL);
14631 if (svn_relpath_skip_ancestor(relpath, dir_relpath))
14633 int locked_levels = svn_sqlite__column_int(stmt, 1);
14634 int row_depth = relpath_depth(relpath);
14636 if (locked_levels == -1
14637 || locked_levels + row_depth >= dir_depth)
14639 *lock_relpath = apr_pstrdup(result_pool, relpath);
14640 SVN_ERR(svn_sqlite__reset(stmt));
14641 return SVN_NO_ERROR;
14645 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14648 *lock_relpath = NULL;
14650 return svn_error_trace(svn_sqlite__reset(stmt));
14653 static svn_error_t *
14654 is_wclocked(svn_boolean_t *locked,
14655 svn_wc__db_wcroot_t *wcroot,
14656 const char *dir_relpath,
14657 apr_pool_t *scratch_pool)
14659 const char *lock_relpath;
14661 SVN_ERR(find_wclock(&lock_relpath, wcroot, dir_relpath,
14662 scratch_pool, scratch_pool));
14663 *locked = (lock_relpath != NULL);
14664 return SVN_NO_ERROR;
14669 svn_wc__db_wclock_find_root(const char **lock_abspath,
14671 const char *local_abspath,
14672 apr_pool_t *result_pool,
14673 apr_pool_t *scratch_pool)
14675 svn_wc__db_wcroot_t *wcroot;
14676 const char *local_relpath;
14677 const char *lock_relpath;
14679 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14680 local_abspath, scratch_pool, scratch_pool));
14681 VERIFY_USABLE_WCROOT(wcroot);
14683 SVN_WC__DB_WITH_TXN(
14684 find_wclock(&lock_relpath, wcroot, local_relpath,
14685 scratch_pool, scratch_pool),
14689 *lock_abspath = NULL;
14691 SVN_ERR(svn_wc__db_from_relpath(lock_abspath, db, wcroot->abspath,
14692 lock_relpath, result_pool, scratch_pool));
14693 return SVN_NO_ERROR;
14698 svn_wc__db_wclocked(svn_boolean_t *locked,
14700 const char *local_abspath,
14701 apr_pool_t *scratch_pool)
14703 svn_wc__db_wcroot_t *wcroot;
14704 const char *local_relpath;
14706 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14707 local_abspath, scratch_pool, scratch_pool));
14708 VERIFY_USABLE_WCROOT(wcroot);
14710 SVN_WC__DB_WITH_TXN(
14711 is_wclocked(locked, wcroot, local_relpath, scratch_pool),
14714 return SVN_NO_ERROR;
14719 svn_wc__db_wclock_release(svn_wc__db_t *db,
14720 const char *local_abspath,
14721 apr_pool_t *scratch_pool)
14723 svn_sqlite__stmt_t *stmt;
14724 svn_wc__db_wcroot_t *wcroot;
14725 const char *local_relpath;
14727 apr_array_header_t *owned_locks;
14729 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14730 local_abspath, scratch_pool, scratch_pool));
14732 VERIFY_USABLE_WCROOT(wcroot);
14734 /* First check and remove the owns-lock information as failure in
14735 removing the db record implies that we have to steal the lock later. */
14736 owned_locks = wcroot->owned_locks;
14737 for (i = 0; i < owned_locks->nelts; i++)
14739 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14740 svn_wc__db_wclock_t);
14742 if (strcmp(lock->local_relpath, local_relpath) == 0)
14746 if (i >= owned_locks->nelts)
14747 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
14748 _("Working copy not locked at '%s'."),
14749 svn_dirent_local_style(local_abspath,
14752 if (i < owned_locks->nelts)
14754 owned_locks->nelts--;
14756 /* Move the last item in the array to the deleted place */
14757 if (owned_locks->nelts > 0)
14758 APR_ARRAY_IDX(owned_locks, i, svn_wc__db_wclock_t) =
14759 APR_ARRAY_IDX(owned_locks, owned_locks->nelts, svn_wc__db_wclock_t);
14762 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14763 STMT_DELETE_WC_LOCK));
14765 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14767 SVN_ERR(svn_sqlite__step_done(stmt));
14769 return SVN_NO_ERROR;
14773 /* Like svn_wc__db_wclock_owns_lock() but taking WCROOT+LOCAL_RELPATH instead
14774 of DB+LOCAL_ABSPATH. */
14776 svn_wc__db_wclock_owns_lock_internal(svn_boolean_t *own_lock,
14777 svn_wc__db_wcroot_t *wcroot,
14778 const char *local_relpath,
14779 svn_boolean_t exact,
14780 apr_pool_t *scratch_pool)
14782 apr_array_header_t *owned_locks;
14787 owned_locks = wcroot->owned_locks;
14788 lock_level = relpath_depth(local_relpath);
14792 for (i = 0; i < owned_locks->nelts; i++)
14794 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14795 svn_wc__db_wclock_t);
14797 if (strcmp(lock->local_relpath, local_relpath) == 0)
14800 return SVN_NO_ERROR;
14806 for (i = 0; i < owned_locks->nelts; i++)
14808 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14809 svn_wc__db_wclock_t);
14811 if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14812 && (lock->levels == -1
14813 || ((relpath_depth(lock->local_relpath) + lock->levels)
14817 return SVN_NO_ERROR;
14822 return SVN_NO_ERROR;
14827 svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
14829 const char *local_abspath,
14830 svn_boolean_t exact,
14831 apr_pool_t *scratch_pool)
14833 svn_wc__db_wcroot_t *wcroot;
14834 const char *local_relpath;
14836 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14837 local_abspath, scratch_pool, scratch_pool));
14840 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
14841 _("The node '%s' was not found."),
14842 svn_dirent_local_style(local_abspath,
14845 VERIFY_USABLE_WCROOT(wcroot);
14847 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(own_lock, wcroot, local_relpath,
14848 exact, scratch_pool));
14850 return SVN_NO_ERROR;
14853 /* The body of svn_wc__db_temp_op_end_directory_update().
14855 static svn_error_t *
14856 end_directory_update(svn_wc__db_wcroot_t *wcroot,
14857 const char *local_relpath,
14858 apr_pool_t *scratch_pool)
14860 svn_sqlite__stmt_t *stmt;
14861 svn_wc__db_status_t base_status;
14863 SVN_ERR(svn_wc__db_base_get_info_internal(&base_status, NULL, NULL, NULL,
14864 NULL, NULL, NULL, NULL, NULL,
14865 NULL, NULL, NULL, NULL, NULL, NULL,
14866 wcroot, local_relpath,
14867 scratch_pool, scratch_pool));
14869 if (base_status == svn_wc__db_status_normal)
14870 return SVN_NO_ERROR;
14872 SVN_ERR_ASSERT(base_status == svn_wc__db_status_incomplete);
14874 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14875 STMT_UPDATE_NODE_BASE_PRESENCE));
14876 SVN_ERR(svn_sqlite__bindf(stmt, "ist", wcroot->wc_id, local_relpath,
14877 presence_map, svn_wc__db_status_normal));
14878 SVN_ERR(svn_sqlite__step_done(stmt));
14880 return SVN_NO_ERROR;
14884 svn_wc__db_temp_op_end_directory_update(svn_wc__db_t *db,
14885 const char *local_dir_abspath,
14886 apr_pool_t *scratch_pool)
14888 svn_wc__db_wcroot_t *wcroot;
14889 const char *local_relpath;
14891 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
14893 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14894 local_dir_abspath, scratch_pool, scratch_pool));
14895 VERIFY_USABLE_WCROOT(wcroot);
14897 SVN_WC__DB_WITH_TXN(
14898 end_directory_update(wcroot, local_relpath, scratch_pool),
14901 SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_empty,
14904 return SVN_NO_ERROR;
14908 /* The body of svn_wc__db_temp_op_start_directory_update().
14910 static svn_error_t *
14911 start_directory_update_txn(svn_wc__db_wcroot_t *wcroot,
14912 const char *local_relpath,
14913 const char *new_repos_relpath,
14914 svn_revnum_t new_rev,
14915 apr_pool_t *scratch_pool)
14917 svn_sqlite__stmt_t *stmt;
14919 /* Note: In the majority of calls, the repos_relpath is unchanged. */
14920 /* ### TODO: Maybe check if we can make repos_relpath NULL. */
14921 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14922 STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH));
14924 SVN_ERR(svn_sqlite__bindf(stmt, "istrs",
14927 presence_map, svn_wc__db_status_incomplete,
14929 new_repos_relpath));
14930 SVN_ERR(svn_sqlite__step_done(stmt));
14932 return SVN_NO_ERROR;
14937 svn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db,
14938 const char *local_abspath,
14939 const char *new_repos_relpath,
14940 svn_revnum_t new_rev,
14941 apr_pool_t *scratch_pool)
14943 svn_wc__db_wcroot_t *wcroot;
14944 const char *local_relpath;
14946 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14947 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_rev));
14948 SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
14950 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14951 local_abspath, scratch_pool, scratch_pool));
14952 VERIFY_USABLE_WCROOT(wcroot);
14954 SVN_WC__DB_WITH_TXN(
14955 start_directory_update_txn(wcroot, local_relpath,
14956 new_repos_relpath, new_rev, scratch_pool),
14959 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
14961 return SVN_NO_ERROR;
14964 /* Helper for svn_wc__db_op_make_copy_internal */
14965 static svn_error_t *
14966 db_move_moved_to(svn_wc__db_wcroot_t *wcroot,
14967 const char *src1_relpath,
14969 const char *src2_relpath,
14971 const char *dst_relpath,
14972 apr_pool_t *scratch_pool)
14974 svn_sqlite__stmt_t *stmt;
14977 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14978 STMT_UPDATE_MOVED_TO_RELPATH));
14979 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
14980 src1_relpath, src1_op_depth));
14981 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
14983 if (affected_rows == 1)
14985 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14986 STMT_UPDATE_MOVED_TO_RELPATH));
14987 SVN_ERR(svn_sqlite__bindf(stmt, "isds", wcroot->wc_id,
14988 src2_relpath, src2_op_depth,
14990 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
14992 if (affected_rows != 1)
14993 return svn_error_create(SVN_ERR_WC_PATH_NOT_FOUND, NULL, NULL);
14995 return SVN_NO_ERROR;
14998 static svn_error_t *
14999 db_move_moved_to_down_recursive(svn_wc__db_wcroot_t *wcroot,
15000 const char *local_relpath,
15001 int new_shadow_layer,
15002 apr_pool_t *scratch_pool)
15004 svn_sqlite__stmt_t *stmt;
15005 svn_boolean_t have_row;
15006 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15008 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15009 STMT_SELECT_MOVED_DESCENDANTS_SRC));
15010 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15011 new_shadow_layer));
15012 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15017 const char *src_relpath;
15018 const char *dst_relpath;
15021 svn_pool_clear(iterpool);
15023 del_op_depth = svn_sqlite__column_int(stmt, 0);
15024 src_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
15025 dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool);
15027 err = svn_error_trace(
15030 src_relpath, del_op_depth,
15031 src_relpath, new_shadow_layer,
15032 dst_relpath, iterpool));
15035 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
15037 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15040 SVN_ERR(svn_sqlite__reset(stmt));
15042 return SVN_NO_ERROR;
15046 /* The body of svn_wc__db_temp_op_make_copy(). This is
15047 used by the update editor when deleting a base node tree would be a
15048 tree-conflict because there are changes to subtrees. This function
15049 inserts a copy of the base node tree below any existing working
15050 subtrees. Given a tree:
15055 A/B normal - normal
15056 A/B/C normal - base-del normal
15057 A/F normal - normal
15058 A/F/G normal - normal
15059 A/F/H normal - base-deleted normal
15060 A/F/E normal - not-present
15064 This function adds layers to A and some of its descendants in an attempt
15065 to make the working copy look like as if it were a copy of the BASE nodes.
15070 A/B normal norm norm
15071 A/B/C normal norm base-del normal
15072 A/F normal norm norm
15073 A/F/G normal norm norm
15074 A/F/H normal norm not-pres
15075 A/F/E normal norm base-del
15077 A/X/Y incomplete incomplete
15079 static svn_error_t *
15080 make_copy_txn(svn_wc__db_wcroot_t *wcroot,
15081 const char *local_relpath,
15082 apr_int64_t last_repos_id,
15083 const char *last_repos_relpath,
15084 svn_revnum_t last_revision,
15086 svn_boolean_t shadowed,
15087 int root_shadow_depth,
15088 apr_pool_t *scratch_pool)
15090 svn_sqlite__stmt_t *stmt;
15091 svn_boolean_t have_row = FALSE;
15092 svn_revnum_t revision;
15093 apr_int64_t repos_id;
15094 const char *repos_relpath;
15095 svn_node_kind_t kind;
15096 int op_depth = relpath_depth(local_relpath);
15098 if (last_op_depth != op_depth)
15100 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15101 STMT_SELECT_DEPTH_NODE));
15102 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15104 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15105 SVN_ERR(svn_sqlite__reset(stmt));
15110 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &kind, &revision,
15111 &repos_relpath, &repos_id, NULL,
15112 NULL, NULL, NULL, NULL, NULL, NULL,
15114 wcroot, local_relpath,
15115 scratch_pool, scratch_pool));
15117 if (last_repos_relpath
15118 && repos_id == last_repos_id
15119 && revision == last_revision)
15121 const char *name = svn_relpath_skip_ancestor(last_repos_relpath,
15124 if (name && strcmp(name, svn_relpath_basename(local_relpath, NULL)) == 0)
15125 op_depth = last_op_depth;
15128 /* Can we add a new copy node at the wanted op-depth? */
15129 if (!have_row || op_depth == last_op_depth)
15133 SVN_ERR(svn_sqlite__get_statement(
15134 &stmt, wcroot->sdb,
15135 STMT_INSERT_WORKING_NODE_FROM_BASE_COPY));
15136 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15138 SVN_ERR(svn_sqlite__step_done(stmt));
15141 SVN_ERR(db_extend_parent_delete(wcroot, local_relpath, kind,
15142 op_depth, scratch_pool));
15144 if (kind == svn_node_dir)
15146 const apr_array_header_t *children;
15147 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15149 SVN_ERR(gather_children(&children, wcroot, local_relpath,
15150 STMT_SELECT_OP_DEPTH_CHILDREN, 0,
15151 scratch_pool, iterpool));
15153 for (i = 0; i < children->nelts; i++)
15155 const char *name = APR_ARRAY_IDX(children, i, const char *);
15156 const char *copy_relpath;
15158 svn_pool_clear(iterpool);
15160 copy_relpath = svn_relpath_join(local_relpath, name, iterpool);
15162 SVN_ERR(make_copy_txn(wcroot, copy_relpath,
15163 repos_id, repos_relpath, revision,
15164 op_depth, shadowed, root_shadow_depth,
15167 svn_pool_destroy(iterpool);
15172 /* Auch... we can't make a copy of whatever comes deeper, as this
15173 op-depth is already filled by something else. Let's hope
15174 the user doesn't mind.
15176 Luckily we know that the moves are already moved to the shadowing
15177 layer, so we can just remove dangling base-deletes if there are
15180 /* BASE_DELETED may be at op_depth, so let's use last_op_depth! */
15181 SVN_ERR(db_move_moved_to_down_recursive(wcroot, local_relpath,
15185 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15186 STMT_DELETE_WORKING_BASE_DELETE));
15187 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15189 SVN_ERR(svn_sqlite__step_done(stmt));
15190 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15191 STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE));
15192 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15194 SVN_ERR(svn_sqlite__step_done(stmt));
15197 /* Insert a not-present node to mark that we don't know what exists here.
15199 We do this last (after recursing), to allow the move fix-up code to
15200 see the original moves. */
15201 if (last_op_depth > 0 && last_op_depth != op_depth)
15203 insert_working_baton_t iwb;
15206 iwb.presence = svn_wc__db_status_not_present;
15207 iwb.op_depth = last_op_depth;
15209 iwb.original_repos_id = repos_id;
15210 iwb.original_repos_relpath = repos_relpath;
15211 iwb.original_revnum = revision;
15214 SVN_ERR(insert_working_node(&iwb, wcroot, local_relpath, scratch_pool));
15217 return SVN_NO_ERROR;
15222 svn_wc__db_op_make_copy_internal(svn_wc__db_wcroot_t *wcroot,
15223 const char *local_relpath,
15224 svn_boolean_t move_move_info,
15225 const svn_skel_t *conflicts,
15226 const svn_skel_t *work_items,
15227 apr_pool_t *scratch_pool)
15229 svn_sqlite__stmt_t *stmt;
15230 svn_boolean_t have_row;
15233 /* The update editor is supposed to call this function when there is
15234 no working node for LOCAL_ABSPATH. */
15235 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15236 STMT_SELECT_WORKING_NODE));
15237 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15238 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15240 op_depth = svn_sqlite__column_int(stmt, 0);
15241 SVN_ERR(svn_sqlite__reset(stmt));
15245 if (op_depth == relpath_depth(local_relpath))
15246 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
15247 _("Modification of '%s' already exists"),
15248 path_for_error_message(wcroot,
15252 /* We have a working layer, but not one at the op-depth of local-relpath,
15253 so we can create a copy by just copying the lower layer */
15255 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15256 STMT_COPY_OP_DEPTH_RECURSIVE));
15257 SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath,
15258 op_depth, relpath_depth(local_relpath)));
15259 SVN_ERR(svn_sqlite__step_done(stmt));
15265 op_depth = relpath_depth(local_relpath);
15266 /* We don't allow copies to contain server-excluded nodes;
15267 the update editor is going to have to bail out. */
15268 SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath,
15271 /* Insert a shadowing layer */
15272 SVN_ERR(svn_sqlite__get_statement(
15273 &stmt, wcroot->sdb,
15274 STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
15276 /* As we are keeping whatever is below, move the*/
15278 SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
15279 wcroot->wc_id, local_relpath,
15281 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
15282 SVN_ERR_ASSERT(affected_rows > 0);
15284 if (!move_move_info)
15285 SVN_ERR(db_move_moved_to_down_recursive(wcroot, local_relpath,
15286 op_depth, scratch_pool));
15289 SVN_ERR(make_copy_txn(wcroot, local_relpath,
15290 INVALID_REPOS_ID, NULL, SVN_INVALID_REVNUM,
15291 op_depth, FALSE, op_depth,
15296 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
15297 conflicts, scratch_pool));
15299 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
15301 return SVN_NO_ERROR;
15306 svn_wc__db_op_make_copy(svn_wc__db_t *db,
15307 const char *local_abspath,
15308 const svn_skel_t *conflicts,
15309 const svn_skel_t *work_items,
15310 apr_pool_t *scratch_pool)
15312 svn_wc__db_wcroot_t *wcroot;
15313 const char *local_relpath;
15315 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15317 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15318 local_abspath, scratch_pool, scratch_pool));
15319 VERIFY_USABLE_WCROOT(wcroot);
15321 SVN_WC__DB_WITH_TXN(
15322 svn_wc__db_op_make_copy_internal(wcroot, local_relpath, FALSE,
15323 conflicts, work_items,
15327 SVN_ERR(flush_entries(wcroot, local_abspath,
15328 svn_depth_infinity, scratch_pool));
15330 return SVN_NO_ERROR;
15334 svn_wc__db_info_below_working(svn_boolean_t *have_base,
15335 svn_boolean_t *have_work,
15336 svn_wc__db_status_t *status,
15338 const char *local_abspath,
15339 apr_pool_t *scratch_pool)
15341 svn_wc__db_wcroot_t *wcroot;
15342 const char *local_relpath;
15344 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15346 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15347 local_abspath, scratch_pool, scratch_pool));
15348 VERIFY_USABLE_WCROOT(wcroot);
15349 SVN_ERR(info_below_working(have_base, have_work, status,
15350 wcroot, local_relpath, -1, scratch_pool));
15352 return SVN_NO_ERROR;
15356 svn_wc__db_get_not_present_descendants(const apr_array_header_t **descendants,
15358 const char *local_abspath,
15359 apr_pool_t *result_pool,
15360 apr_pool_t *scratch_pool)
15362 svn_wc__db_wcroot_t *wcroot;
15363 const char *local_relpath;
15364 svn_sqlite__stmt_t *stmt;
15365 svn_boolean_t have_row;
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_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15374 STMT_SELECT_NOT_PRESENT_DESCENDANTS));
15376 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
15379 relpath_depth(local_relpath)));
15381 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15385 apr_array_header_t *paths;
15387 paths = apr_array_make(result_pool, 4, sizeof(const char*));
15390 const char *found_relpath = svn_sqlite__column_text(stmt, 0, NULL);
15392 APR_ARRAY_PUSH(paths, const char *)
15393 = apr_pstrdup(result_pool, svn_relpath_skip_ancestor(
15394 local_relpath, found_relpath));
15396 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15399 *descendants = paths;
15402 *descendants = apr_array_make(result_pool, 0, sizeof(const char*));
15404 return svn_error_trace(svn_sqlite__reset(stmt));
15408 /* Like svn_wc__db_min_max_revisions(),
15409 * but accepts a WCROOT/LOCAL_RELPATH pair. */
15410 static svn_error_t *
15411 get_min_max_revisions(svn_revnum_t *min_revision,
15412 svn_revnum_t *max_revision,
15413 svn_wc__db_wcroot_t *wcroot,
15414 const char *local_relpath,
15415 svn_boolean_t committed,
15416 apr_pool_t *scratch_pool)
15418 svn_sqlite__stmt_t *stmt;
15419 svn_revnum_t min_rev, max_rev;
15421 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15422 STMT_SELECT_MIN_MAX_REVISIONS));
15423 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15424 SVN_ERR(svn_sqlite__step_row(stmt));
15428 min_rev = svn_sqlite__column_revnum(stmt, 2);
15429 max_rev = svn_sqlite__column_revnum(stmt, 3);
15433 min_rev = svn_sqlite__column_revnum(stmt, 0);
15434 max_rev = svn_sqlite__column_revnum(stmt, 1);
15437 /* The statement returns exactly one row. */
15438 SVN_ERR(svn_sqlite__reset(stmt));
15441 *min_revision = min_rev;
15443 *max_revision = max_rev;
15445 return SVN_NO_ERROR;
15450 svn_wc__db_min_max_revisions(svn_revnum_t *min_revision,
15451 svn_revnum_t *max_revision,
15453 const char *local_abspath,
15454 svn_boolean_t committed,
15455 apr_pool_t *scratch_pool)
15457 svn_wc__db_wcroot_t *wcroot;
15458 const char *local_relpath;
15460 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15462 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15464 scratch_pool, scratch_pool));
15465 VERIFY_USABLE_WCROOT(wcroot);
15467 return svn_error_trace(get_min_max_revisions(min_revision, max_revision,
15468 wcroot, local_relpath,
15469 committed, scratch_pool));
15473 /* Set *IS_SPARSE_CHECKOUT TRUE if LOCAL_RELPATH or any of the nodes
15474 * within LOCAL_RELPATH is sparse, FALSE otherwise. */
15475 static svn_error_t *
15476 is_sparse_checkout_internal(svn_boolean_t *is_sparse_checkout,
15477 svn_wc__db_wcroot_t *wcroot,
15478 const char *local_relpath,
15479 apr_pool_t *scratch_pool)
15481 svn_sqlite__stmt_t *stmt;
15482 svn_boolean_t have_row;
15484 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15485 STMT_HAS_SPARSE_NODES));
15486 SVN_ERR(svn_sqlite__bindf(stmt, "is",
15489 /* If this query returns a row, the working copy is sparse. */
15490 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15491 *is_sparse_checkout = have_row;
15492 SVN_ERR(svn_sqlite__reset(stmt));
15494 return SVN_NO_ERROR;
15498 /* Like svn_wc__db_has_switched_subtrees(),
15499 * but accepts a WCROOT/LOCAL_RELPATH pair. */
15500 static svn_error_t *
15501 has_switched_subtrees(svn_boolean_t *is_switched,
15502 svn_wc__db_wcroot_t *wcroot,
15503 const char *local_relpath,
15504 const char *trail_url,
15505 apr_pool_t *scratch_pool)
15507 svn_sqlite__stmt_t *stmt;
15508 svn_boolean_t have_row;
15509 apr_int64_t repos_id;
15510 const char *repos_relpath;
15512 /* Optional argument handling for caller */
15514 return SVN_NO_ERROR;
15516 *is_switched = FALSE;
15518 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
15519 &repos_relpath, &repos_id,
15520 NULL, NULL, NULL, NULL, NULL,
15521 NULL, NULL, NULL, NULL, NULL,
15522 wcroot, local_relpath,
15523 scratch_pool, scratch_pool));
15525 /* First do the cheap check where we only need info on the origin itself */
15526 if (trail_url != NULL)
15528 const char *repos_root_url;
15530 apr_size_t len1, len2;
15532 /* If the trailing part of the URL of the working copy directory
15533 does not match the given trailing URL then the whole working
15534 copy is switched. */
15536 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot,
15537 repos_id, scratch_pool));
15538 url = svn_path_url_add_component2(repos_root_url, repos_relpath,
15541 len1 = strlen(trail_url);
15542 len2 = strlen(url);
15543 if ((len1 > len2) || strcmp(url + len2 - len1, trail_url))
15545 *is_switched = TRUE;
15546 return SVN_NO_ERROR;
15550 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_HAS_SWITCHED));
15551 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath, repos_relpath));
15552 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15554 *is_switched = TRUE;
15555 SVN_ERR(svn_sqlite__reset(stmt));
15557 return SVN_NO_ERROR;
15562 svn_wc__db_has_switched_subtrees(svn_boolean_t *is_switched,
15564 const char *local_abspath,
15565 const char *trail_url,
15566 apr_pool_t *scratch_pool)
15568 svn_wc__db_wcroot_t *wcroot;
15569 const char *local_relpath;
15571 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15573 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15575 scratch_pool, scratch_pool));
15576 VERIFY_USABLE_WCROOT(wcroot);
15578 return svn_error_trace(has_switched_subtrees(is_switched, wcroot,
15579 local_relpath, trail_url,
15584 svn_wc__db_get_excluded_subtrees(apr_hash_t **excluded_subtrees,
15586 const char *local_abspath,
15587 apr_pool_t *result_pool,
15588 apr_pool_t *scratch_pool)
15590 svn_wc__db_wcroot_t *wcroot;
15591 const char *local_relpath;
15592 svn_sqlite__stmt_t *stmt;
15593 svn_boolean_t have_row;
15595 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15596 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15598 scratch_pool, scratch_pool));
15599 VERIFY_USABLE_WCROOT(wcroot);
15601 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15602 STMT_SELECT_ALL_EXCLUDED_DESCENDANTS));
15603 SVN_ERR(svn_sqlite__bindf(stmt, "is",
15606 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15609 *excluded_subtrees = apr_hash_make(result_pool);
15611 *excluded_subtrees = NULL;
15615 const char *abs_path =
15616 svn_dirent_join(wcroot->abspath,
15617 svn_sqlite__column_text(stmt, 0, NULL),
15619 svn_hash_sets(*excluded_subtrees, abs_path, abs_path);
15620 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15623 SVN_ERR(svn_sqlite__reset(stmt));
15624 return SVN_NO_ERROR;
15627 /* Like svn_wc__db_has_db_mods(),
15628 * but accepts a WCROOT/LOCAL_RELPATH pair.
15629 * ### This needs a DB as well as a WCROOT/RELPATH pair... */
15630 static svn_error_t *
15631 has_db_mods(svn_boolean_t *is_modified,
15632 svn_wc__db_wcroot_t *wcroot,
15633 const char *local_relpath,
15634 apr_pool_t *scratch_pool)
15636 svn_sqlite__stmt_t *stmt;
15638 /* Check for additions or deletions. */
15639 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15640 STMT_SUBTREE_HAS_TREE_MODIFICATIONS));
15641 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15642 /* If this query returns a row, the working copy is modified. */
15643 SVN_ERR(svn_sqlite__step(is_modified, stmt));
15644 SVN_ERR(svn_sqlite__reset(stmt));
15646 if (! *is_modified)
15648 /* Check for property modifications. */
15649 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15650 STMT_SUBTREE_HAS_PROP_MODIFICATIONS));
15651 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15652 /* If this query returns a row, the working copy is modified. */
15653 SVN_ERR(svn_sqlite__step(is_modified, stmt));
15654 SVN_ERR(svn_sqlite__reset(stmt));
15657 return SVN_NO_ERROR;
15662 svn_wc__db_has_db_mods(svn_boolean_t *is_modified,
15664 const char *local_abspath,
15665 apr_pool_t *scratch_pool)
15667 svn_wc__db_wcroot_t *wcroot;
15668 const char *local_relpath;
15670 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15672 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15674 scratch_pool, scratch_pool));
15675 VERIFY_USABLE_WCROOT(wcroot);
15677 return svn_error_trace(has_db_mods(is_modified, wcroot, local_relpath,
15682 /* The body of svn_wc__db_revision_status().
15684 static svn_error_t *
15685 revision_status_txn(svn_revnum_t *min_revision,
15686 svn_revnum_t *max_revision,
15687 svn_boolean_t *is_sparse_checkout,
15688 svn_boolean_t *is_modified,
15689 svn_boolean_t *is_switched,
15690 svn_wc__db_wcroot_t *wcroot,
15691 const char *local_relpath,
15693 const char *trail_url,
15694 svn_boolean_t committed,
15695 apr_pool_t *scratch_pool)
15698 svn_boolean_t exists;
15700 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
15704 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
15705 _("The node '%s' was not found."),
15706 path_for_error_message(wcroot, local_relpath,
15710 /* Determine mixed-revisionness. */
15711 SVN_ERR(get_min_max_revisions(min_revision, max_revision, wcroot,
15712 local_relpath, committed, scratch_pool));
15714 /* Determine sparseness. */
15715 SVN_ERR(is_sparse_checkout_internal(is_sparse_checkout, wcroot,
15716 local_relpath, scratch_pool));
15718 /* Check for switched nodes. */
15720 err = has_switched_subtrees(is_switched, wcroot, local_relpath,
15721 trail_url, scratch_pool);
15725 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
15726 return svn_error_trace(err);
15728 svn_error_clear(err); /* No Base node, but no fatal error */
15729 *is_switched = FALSE;
15733 /* Check for db mods. */
15734 SVN_ERR(has_db_mods(is_modified, wcroot, local_relpath, scratch_pool));
15736 return SVN_NO_ERROR;
15741 svn_wc__db_revision_status(svn_revnum_t *min_revision,
15742 svn_revnum_t *max_revision,
15743 svn_boolean_t *is_sparse_checkout,
15744 svn_boolean_t *is_modified,
15745 svn_boolean_t *is_switched,
15747 const char *local_abspath,
15748 const char *trail_url,
15749 svn_boolean_t committed,
15750 apr_pool_t *scratch_pool)
15752 svn_wc__db_wcroot_t *wcroot;
15753 const char *local_relpath;
15755 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15757 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15759 scratch_pool, scratch_pool));
15760 VERIFY_USABLE_WCROOT(wcroot);
15762 SVN_WC__DB_WITH_TXN(
15763 revision_status_txn(min_revision, max_revision,
15764 is_sparse_checkout, is_modified, is_switched,
15765 wcroot, local_relpath, db,
15766 trail_url, committed,
15769 return SVN_NO_ERROR;
15774 svn_wc__db_base_get_lock_tokens_recursive(apr_hash_t **lock_tokens,
15776 const char *local_abspath,
15777 apr_pool_t *result_pool,
15778 apr_pool_t *scratch_pool)
15780 svn_wc__db_wcroot_t *wcroot;
15781 const char *local_relpath;
15782 svn_sqlite__stmt_t *stmt;
15783 svn_boolean_t have_row;
15784 apr_int64_t last_repos_id = INVALID_REPOS_ID;
15785 const char *last_repos_root_url = NULL;
15787 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15789 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15791 scratch_pool, scratch_pool));
15792 VERIFY_USABLE_WCROOT(wcroot);
15794 *lock_tokens = apr_hash_make(result_pool);
15796 /* Fetch all the lock tokens in and under LOCAL_RELPATH. */
15797 SVN_ERR(svn_sqlite__get_statement(
15798 &stmt, wcroot->sdb,
15799 STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE));
15801 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15802 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15805 apr_int64_t child_repos_id = svn_sqlite__column_int64(stmt, 0);
15806 const char *child_relpath = svn_sqlite__column_text(stmt, 1, NULL);
15807 const char *lock_token = svn_sqlite__column_text(stmt, 2, result_pool);
15809 if (child_repos_id != last_repos_id)
15811 svn_error_t *err = svn_wc__db_fetch_repos_info(&last_repos_root_url,
15818 return svn_error_trace(
15819 svn_error_compose_create(err,
15820 svn_sqlite__reset(stmt)));
15823 last_repos_id = child_repos_id;
15826 SVN_ERR_ASSERT(last_repos_root_url != NULL);
15827 svn_hash_sets(*lock_tokens,
15828 svn_path_url_add_component2(last_repos_root_url,
15829 child_relpath, result_pool),
15832 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15834 return svn_sqlite__reset(stmt);
15838 /* If EXPRESSION is false, cause the caller to return an SVN_ERR_WC_CORRUPT
15839 * error, showing EXPRESSION and the caller's LOCAL_RELPATH in the message. */
15840 #define VERIFY(expression) \
15842 if (! (expression)) \
15843 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, \
15844 _("database inconsistency at local_relpath='%s' verifying " \
15845 "expression '%s'"), local_relpath, #expression); \
15849 /* Verify consistency of the metadata concerning WCROOT. This is intended
15850 * for use only during testing and debugging, so is not intended to be
15853 * This code is a complement to any verification that we can do in SQLite
15854 * triggers. See, for example, 'wc-checks.sql'.
15856 * Some more verification steps we might want to add are:
15858 * * on every ACTUAL row (except root): a NODES row exists at its parent path
15859 * * the op-depth root must always exist and every intermediate too
15861 static svn_error_t *
15862 verify_wcroot(svn_wc__db_wcroot_t *wcroot,
15863 apr_pool_t *scratch_pool)
15865 svn_sqlite__stmt_t *stmt;
15866 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15868 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15869 STMT_SELECT_ALL_NODES));
15870 SVN_ERR(svn_sqlite__bindf(stmt, "i", wcroot->wc_id));
15873 svn_boolean_t have_row;
15874 const char *local_relpath, *parent_relpath;
15877 svn_pool_clear(iterpool);
15879 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15883 op_depth = svn_sqlite__column_int(stmt, 0);
15884 local_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
15885 parent_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
15887 /* Verify parent_relpath is the parent path of local_relpath */
15888 VERIFY((parent_relpath == NULL)
15889 ? (local_relpath[0] == '\0')
15890 : (strcmp(svn_relpath_dirname(local_relpath, iterpool),
15891 parent_relpath) == 0));
15893 /* Verify op_depth <= the tree depth of local_relpath */
15894 VERIFY(op_depth <= relpath_depth(local_relpath));
15896 /* Verify parent_relpath refers to a row that exists */
15897 /* TODO: Verify there is a suitable parent row - e.g. has op_depth <=
15898 * the child's and a suitable presence */
15899 if (parent_relpath && svn_sqlite__column_is_null(stmt, 3))
15901 svn_sqlite__stmt_t *stmt2;
15902 svn_boolean_t have_a_parent_row;
15904 SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
15905 STMT_SELECT_NODE_INFO));
15906 SVN_ERR(svn_sqlite__bindf(stmt2, "is", wcroot->wc_id,
15908 SVN_ERR(svn_sqlite__step(&have_a_parent_row, stmt2));
15909 VERIFY(have_a_parent_row);
15910 SVN_ERR(svn_sqlite__reset(stmt2));
15913 svn_pool_destroy(iterpool);
15915 return svn_error_trace(svn_sqlite__reset(stmt));
15919 svn_wc__db_verify(svn_wc__db_t *db,
15920 const char *wri_abspath,
15921 apr_pool_t *scratch_pool)
15923 svn_wc__db_wcroot_t *wcroot;
15924 const char *local_relpath;
15926 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15928 scratch_pool, scratch_pool));
15929 VERIFY_USABLE_WCROOT(wcroot);
15931 SVN_ERR(verify_wcroot(wcroot, scratch_pool));
15932 return SVN_NO_ERROR;
15937 svn_wc__db_verify_db_full_internal(svn_wc__db_wcroot_t *wcroot,
15938 svn_wc__db_verify_cb_t callback,
15940 apr_pool_t *scratch_pool)
15942 svn_sqlite__stmt_t *stmt;
15943 svn_boolean_t have_row;
15944 svn_error_t *err = NULL;
15945 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15947 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_STATIC_VERIFY));
15948 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15952 const char *local_relpath;
15953 int op_depth = svn_sqlite__column_int(stmt, 1);
15954 int id = svn_sqlite__column_int(stmt, 2);
15957 svn_pool_clear(iterpool);
15959 local_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
15960 msg = svn_sqlite__column_text(stmt, 3, scratch_pool);
15962 err = callback(baton, wcroot->abspath, local_relpath, op_depth,
15963 id, msg, iterpool);
15968 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15971 svn_pool_destroy(iterpool);
15973 return svn_error_trace(
15974 svn_error_compose_create(err, svn_sqlite__reset(stmt)));
15978 svn_wc__db_verify_db_full(svn_wc__db_t *db,
15979 const char *wri_abspath,
15980 svn_wc__db_verify_cb_t callback,
15982 apr_pool_t *scratch_pool)
15984 svn_wc__db_wcroot_t *wcroot;
15985 const char *local_relpath;
15987 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
15989 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15990 wri_abspath, scratch_pool, scratch_pool));
15991 VERIFY_USABLE_WCROOT(wcroot);
15993 return svn_error_trace(
15994 svn_wc__db_verify_db_full_internal(wcroot, callback, baton,
15999 svn_wc__db_bump_format(int *result_format,
16000 svn_boolean_t *bumped_format,
16002 const char *wcroot_abspath,
16003 apr_pool_t *scratch_pool)
16005 svn_sqlite__db_t *sdb;
16010 *bumped_format = FALSE;
16012 /* Do not scan upwards for a working copy root here to prevent accidental
16013 * upgrades of any working copies the WCROOT might be nested in.
16014 * Just try to open a DB at the specified path instead. */
16015 err = svn_wc__db_util_open_db(&sdb, wcroot_abspath, SDB_FILE,
16016 svn_sqlite__mode_readwrite,
16017 TRUE, /* exclusive */
16018 0, /* default timeout */
16019 NULL, /* my statements */
16020 scratch_pool, scratch_pool);
16024 apr_hash_t *entries;
16026 /* Could not open an sdb. Check for an entries file instead. */
16027 err2 = svn_wc__read_entries_old(&entries, wcroot_abspath,
16028 scratch_pool, scratch_pool);
16029 if (err2 || apr_hash_count(entries) == 0)
16030 return svn_error_createf(SVN_ERR_WC_INVALID_OP_ON_CWD,
16031 svn_error_compose_create(err, err2),
16032 _("Can't upgrade '%s' as it is not a working copy root"),
16033 svn_dirent_local_style(wcroot_abspath, scratch_pool));
16035 /* An entries file was found. This is a pre-wc-ng working copy
16036 * so suggest an upgrade. */
16037 return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, err,
16038 _("Working copy '%s' is too old and must be upgraded to "
16039 "at least format %d, as created by Subversion %s"),
16040 svn_dirent_local_style(wcroot_abspath, scratch_pool),
16041 SVN_WC__WC_NG_VERSION,
16042 svn_wc__version_string_from_format(SVN_WC__WC_NG_VERSION));
16045 SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool));
16046 err = svn_wc__upgrade_sdb(result_format, wcroot_abspath,
16047 sdb, format, scratch_pool);
16049 if (err == SVN_NO_ERROR && bumped_format)
16050 *bumped_format = (*result_format > format);
16052 /* Make sure we return a different error than expected for upgrades from
16054 if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
16055 err = svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, err,
16056 _("Working copy upgrade failed"));
16058 err = svn_error_compose_create(err, svn_sqlite__close(sdb));
16060 return svn_error_trace(err);
16064 svn_wc__db_vacuum(svn_wc__db_t *db,
16065 const char *local_abspath,
16066 apr_pool_t *scratch_pool)
16068 svn_wc__db_wcroot_t *wcroot;
16069 const char *local_relpath;
16071 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
16073 scratch_pool, scratch_pool));
16074 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_VACUUM));
16076 return SVN_NO_ERROR;
16079 /* Item queued with svn_wc__db_commit_queue_add */
16080 typedef struct commit_queue_item_t
16082 const char *local_relpath;
16083 svn_boolean_t recurse; /* Use legacy recursion */
16084 svn_boolean_t committed; /* Process the node as committed */
16085 svn_boolean_t remove_lock; /* Remove existing lock on node */
16086 svn_boolean_t remove_changelist; /* Remove changelist on node */
16088 /* The pristine text checksum. NULL if the old value should be kept
16089 and for directories */
16090 const svn_checksum_t *new_sha1_checksum;
16092 apr_hash_t *new_dav_cache; /* New DAV cache for the node */
16093 } commit_queue_item_t;
16095 /* The queue definition for vn_wc__db_create_commit_queue,
16096 svn_wc__db_commit_queue_add and finally svn_wc__db_process_commit_queue */
16097 struct svn_wc__db_commit_queue_t
16099 svn_wc__db_wcroot_t *wcroot; /* Wcroot for ITEMS */
16100 apr_array_header_t *items; /* List of commit_queue_item_t* */
16101 svn_boolean_t have_recurse; /* Is one or more item[x]->recurse TRUE? */
16104 /* Create a new svn_wc__db_commit_queue_t instance in RESULT_POOL for the
16105 working copy specified with WRI_ABSPATH */
16107 svn_wc__db_create_commit_queue(svn_wc__db_commit_queue_t **queue,
16109 const char *wri_abspath,
16110 apr_pool_t *result_pool,
16111 apr_pool_t *scratch_pool)
16113 svn_wc__db_wcroot_t *wcroot;
16114 const char *local_relpath;
16115 svn_wc__db_commit_queue_t *q;
16117 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
16119 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
16120 wri_abspath, result_pool, scratch_pool));
16121 VERIFY_USABLE_WCROOT(wcroot);
16123 q = apr_pcalloc(result_pool, sizeof(*q));
16125 SVN_ERR_ASSERT(wcroot->sdb);
16127 q->wcroot = wcroot;
16128 q->items = apr_array_make(result_pool, 64,
16129 sizeof(commit_queue_item_t*));
16130 q->have_recurse = FALSE;
16133 return SVN_NO_ERROR;
16137 svn_wc__db_commit_queue_add(svn_wc__db_commit_queue_t *queue,
16138 const char *local_abspath,
16139 svn_boolean_t recurse,
16140 svn_boolean_t is_commited,
16141 svn_boolean_t remove_lock,
16142 svn_boolean_t remove_changelist,
16143 const svn_checksum_t *new_sha1_checksum,
16144 apr_hash_t *new_dav_cache,
16145 apr_pool_t *result_pool,
16146 apr_pool_t *scratch_pool)
16148 commit_queue_item_t *cqi;
16149 const char *local_relpath;
16151 local_relpath = svn_dirent_skip_ancestor(queue->wcroot->abspath,
16154 if (! local_relpath)
16155 return svn_error_createf(
16156 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
16157 _("The path '%s' is not in the working copy '%s'"),
16158 svn_dirent_local_style(local_abspath, scratch_pool),
16159 svn_dirent_local_style(queue->wcroot->abspath, scratch_pool));
16161 cqi = apr_pcalloc(result_pool, sizeof(*cqi));
16162 cqi->local_relpath = local_relpath;
16163 cqi->recurse = recurse;
16164 cqi->committed = is_commited;
16165 cqi->remove_lock = remove_lock;
16166 cqi->remove_changelist = remove_changelist;
16167 cqi->new_sha1_checksum = new_sha1_checksum;
16168 cqi->new_dav_cache = new_dav_cache;
16170 queue->have_recurse |= recurse;
16172 APR_ARRAY_PUSH(queue->items, commit_queue_item_t *) = cqi;
16173 return SVN_NO_ERROR;
16176 /*** Finishing updates and commits. ***/
16178 /* Post process an item that is committed in the repository. Collapse layers into
16179 * BASE. Queue work items that will finish a commit of the file or directory
16180 * LOCAL_ABSPATH in DB:
16182 static svn_error_t *
16183 process_committed_leaf(svn_wc__db_t *db,
16184 svn_wc__db_wcroot_t *wcroot,
16185 const char *local_relpath,
16186 svn_boolean_t via_recurse,
16187 svn_wc__db_status_t status,
16188 svn_node_kind_t kind,
16189 svn_boolean_t prop_mods,
16190 const svn_checksum_t *old_checksum,
16191 svn_revnum_t new_revnum,
16192 apr_time_t new_changed_date,
16193 const char *new_changed_author,
16194 apr_hash_t *new_dav_cache,
16195 svn_boolean_t remove_lock,
16196 svn_boolean_t remove_changelist,
16197 const svn_checksum_t *checksum,
16198 apr_pool_t *scratch_pool)
16200 svn_revnum_t new_changed_rev = new_revnum;
16201 svn_skel_t *work_item = NULL;
16204 const char *lock_relpath;
16205 svn_boolean_t locked;
16207 if (kind == svn_node_dir)
16208 lock_relpath = local_relpath;
16210 lock_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
16212 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot,
16213 lock_relpath, FALSE,
16217 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
16218 _("No write-lock in '%s'"),
16219 path_for_error_message(wcroot, local_relpath,
16222 SVN_ERR(flush_entries(wcroot, lock_relpath, svn_depth_empty,
16226 if (status == svn_wc__db_status_not_present)
16228 /* We are committing the leaf of a copy operation.
16229 We leave the not-present marker to allow pulling in excluded
16230 children of a copy.
16232 The next update will remove the not-present marker. */
16234 return SVN_NO_ERROR;
16237 SVN_ERR_ASSERT(status == svn_wc__db_status_normal
16238 || status == svn_wc__db_status_incomplete
16239 || status == svn_wc__db_status_added
16240 || status == svn_wc__db_status_deleted);
16242 if (kind != svn_node_dir
16243 && status != svn_wc__db_status_deleted)
16245 /* If we sent a delta (meaning: post-copy modification),
16246 then this file will appear in the queue and so we should have
16247 its checksum already. */
16248 if (checksum == NULL)
16250 /* It was copied and not modified. We must have a text
16251 base for it. And the node should have a checksum. */
16252 SVN_ERR_ASSERT(old_checksum != NULL);
16254 checksum = old_checksum;
16256 /* Is the node completely unmodified and are we recursing? */
16257 if (via_recurse && !prop_mods)
16259 /* If a copied node itself is not modified, but the op_root of
16260 the copy is committed we have to make sure that changed_rev,
16261 changed_date and changed_author don't change or the working
16262 copy used for committing will show different last modified
16263 information then a clean checkout of exactly the same
16264 revisions. (Issue #3676) */
16266 SVN_ERR(svn_wc__db_read_info_internal(
16267 NULL, NULL, NULL, NULL, NULL,
16270 &new_changed_author, NULL, NULL,
16271 NULL, NULL, NULL, NULL, NULL,
16272 NULL, NULL, NULL, NULL,
16273 NULL, NULL, NULL, NULL,
16275 wcroot, local_relpath,
16276 scratch_pool, scratch_pool));
16280 SVN_ERR(svn_wc__wq_build_file_commit(&work_item,
16281 db, svn_dirent_join(wcroot->abspath,
16285 scratch_pool, scratch_pool));
16288 /* The new text base will be found in the pristine store by its checksum. */
16289 SVN_ERR(commit_node(wcroot, local_relpath,
16290 new_revnum, new_changed_rev,
16291 new_changed_date, new_changed_author,
16294 !remove_changelist,
16299 return SVN_NO_ERROR;
16302 /** Internal helper for svn_wc_process_committed_queue2().
16303 * Bump a commit item, collapsing local changes with the new repository
16304 * information to a new BASE node.
16306 * @a new_date is the (server-side) date of the new revision, or 0.
16308 * @a rev_author is the (server-side) author of the new
16309 * revision; it may be @c NULL.
16311 * @a new_dav_cache is a hash of all the new dav properties for LOCAL_RELPATH.
16313 * If @a remove_lock is set, release any user locks on @a
16314 * local_abspath; otherwise keep them during processing.
16316 * If @a remove_changelist is set, clear any changeset assignments
16317 * from @a local_abspath; otherwise, keep such assignments.
16319 * If @a new_sha1_checksum is non-NULL, use it to identify the node's pristine
16322 * Set TOP_OF_RECURSE to TRUE to show that this the top of a possibly
16323 * recursive commit operation. (Part of the legacy recurse handling)
16325 static svn_error_t *
16326 process_committed_internal(svn_wc__db_t *db,
16327 svn_wc__db_wcroot_t *wcroot,
16328 const char *local_relpath,
16329 svn_boolean_t recurse,
16330 svn_boolean_t top_of_recurse,
16331 svn_revnum_t new_revnum,
16332 apr_time_t new_date,
16333 const char *rev_author,
16334 apr_hash_t *new_dav_cache,
16335 svn_boolean_t remove_lock,
16336 svn_boolean_t remove_changelist,
16337 const svn_checksum_t *new_sha1_checksum,
16338 apr_hash_t *items_by_relpath,
16339 apr_pool_t *scratch_pool)
16341 svn_wc__db_status_t status;
16342 svn_node_kind_t kind;
16343 const svn_checksum_t *old_checksum;
16344 svn_boolean_t prop_mods;
16346 SVN_ERR(svn_wc__db_read_info_internal(&status, &kind, NULL, NULL, NULL, NULL, NULL,
16347 NULL, NULL, &old_checksum, NULL, NULL,
16348 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
16349 NULL, &prop_mods, NULL, NULL, NULL,
16350 wcroot, local_relpath,
16351 scratch_pool, scratch_pool));
16353 /* NOTE: be wary of making crazy semantic changes in this function, since
16354 svn_wc_process_committed4() calls this. */
16356 SVN_ERR(process_committed_leaf(db, wcroot, local_relpath, !top_of_recurse,
16357 status, kind, prop_mods, old_checksum,
16358 new_revnum, new_date, rev_author,
16360 remove_lock, remove_changelist,
16364 /* Only check for recursion on nodes that have children */
16365 if (kind != svn_node_dir
16366 || status == svn_wc__db_status_not_present
16367 || status == svn_wc__db_status_excluded
16368 || status == svn_wc__db_status_server_excluded
16369 /* Node deleted -> then no longer a directory */
16370 || status == svn_wc__db_status_deleted)
16372 return SVN_NO_ERROR;
16377 const apr_array_header_t *children;
16378 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
16381 /* Read PATH's entries; this is the absolute path. */
16382 SVN_ERR(gather_children(&children, wcroot, local_relpath,
16383 STMT_SELECT_NODE_CHILDREN, -1,
16384 scratch_pool, iterpool));
16386 /* Recursively loop over all children. */
16387 for (i = 0; i < children->nelts; i++)
16389 const char *name = APR_ARRAY_IDX(children, i, const char *);
16390 const char *this_relpath;
16391 const commit_queue_item_t *cqi;
16393 svn_pool_clear(iterpool);
16395 this_relpath = svn_dirent_join(local_relpath, name, iterpool);
16397 new_sha1_checksum = NULL;
16398 cqi = svn_hash_gets(items_by_relpath, this_relpath);
16401 new_sha1_checksum = cqi->new_sha1_checksum;
16403 /* Recurse. Pass NULL for NEW_DAV_CACHE, because the
16404 ones present in the current call are only applicable to
16405 this one committed item. */
16406 SVN_ERR(process_committed_internal(
16407 db, wcroot, this_relpath,
16408 TRUE /* recurse */,
16409 FALSE /* top_of_recurse */,
16410 new_revnum, new_date,
16412 NULL /* new_dav_cache */,
16413 FALSE /* remove_lock */,
16420 svn_pool_destroy(iterpool);
16423 return SVN_NO_ERROR;
16426 /* Return TRUE if any item of QUEUE is a parent of ITEM and will be
16427 processed recursively, return FALSE otherwise.
16429 The algorithmic complexity of this search implementation is O(queue
16430 length), but it's quite quick.
16432 static svn_boolean_t
16433 have_recursive_parent(const apr_array_header_t *all_items,
16434 const commit_queue_item_t *item,
16435 apr_pool_t *scratch_pool)
16437 const char *local_relpath = item->local_relpath;
16440 for (i = 0; i < all_items->nelts; i++)
16442 const commit_queue_item_t *qi
16443 = APR_ARRAY_IDX(all_items, i, const commit_queue_item_t *);
16448 if (qi->recurse && svn_relpath_skip_ancestor(qi->local_relpath,
16458 /* Compare function for svn_sort__array */
16460 compare_queue_items(const void *v1,
16463 const commit_queue_item_t *cqi1
16464 = *(const commit_queue_item_t **)v1;
16465 const commit_queue_item_t *cqi2
16466 = *(const commit_queue_item_t **)v2;
16468 return svn_path_compare_paths(cqi1->local_relpath, cqi2->local_relpath);
16471 /* Internal, locked version of svn_wc__db_process_commit_queue */
16472 static svn_error_t *
16473 db_process_commit_queue(svn_wc__db_t *db,
16474 svn_wc__db_commit_queue_t *queue,
16475 svn_revnum_t new_revnum,
16476 apr_time_t new_date,
16477 const char *new_author,
16478 apr_pool_t *scratch_pool)
16480 apr_hash_t *items_by_relpath = NULL;
16482 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
16484 svn_sort__array(queue->items, compare_queue_items);
16486 if (queue->have_recurse)
16488 items_by_relpath = apr_hash_make(scratch_pool);
16490 for (j = 0; j < queue->items->nelts; j++)
16492 commit_queue_item_t *cqi
16493 = APR_ARRAY_IDX(queue->items, j, commit_queue_item_t *);
16495 svn_hash_sets(items_by_relpath, cqi->local_relpath, cqi);
16499 for (j = 0; j < queue->items->nelts; j++)
16501 commit_queue_item_t *cqi
16502 = APR_ARRAY_IDX(queue->items, j, commit_queue_item_t *);
16504 svn_pool_clear(iterpool);
16506 /* Skip this item if it is a child of a recursive item, because it has
16507 been (or will be) accounted for when that recursive item was (or
16508 will be) processed. */
16509 if (queue->have_recurse && have_recursive_parent(queue->items, cqi,
16513 if (!cqi->committed)
16515 if (cqi->remove_lock)
16517 svn_skel_t *work_item;
16519 SVN_ERR(svn_wc__wq_build_sync_file_flags(
16523 queue->wcroot->abspath,
16524 cqi->local_relpath,
16526 iterpool, iterpool));
16528 lock_remove_txn(queue->wcroot, cqi->local_relpath, work_item,
16531 if (cqi->remove_changelist)
16532 SVN_ERR(svn_wc__db_op_set_changelist(db,
16534 queue->wcroot->abspath,
16535 cqi->local_relpath,
16539 NULL, NULL, /* notify */
16540 NULL, NULL, /* cancel */
16545 SVN_ERR(process_committed_internal(
16546 db, queue->wcroot, cqi->local_relpath,
16548 TRUE /* top_of_recurse */,
16549 new_revnum, new_date, new_author,
16550 cqi->new_dav_cache,
16552 cqi->remove_changelist,
16553 cqi->new_sha1_checksum,
16559 svn_pool_destroy(iterpool);
16561 return SVN_NO_ERROR;
16565 svn_wc__db_process_commit_queue(svn_wc__db_t *db,
16566 svn_wc__db_commit_queue_t *queue,
16567 svn_revnum_t new_revnum,
16568 apr_time_t new_date,
16569 const char *new_author,
16570 apr_pool_t *scratch_pool)
16572 SVN_WC__DB_WITH_TXN(db_process_commit_queue(db, queue,
16573 new_revnum, new_date,
16574 new_author, scratch_pool),
16577 return SVN_NO_ERROR;
16581 svn_wc__find_repos_node_in_wc(apr_array_header_t **local_abspath_list,
16583 const char *wri_abspath,
16584 const char *repos_relpath,
16585 apr_pool_t *result_pool,
16586 apr_pool_t *scratch_pool)
16588 svn_wc__db_wcroot_t *wcroot;
16589 const char *wri_relpath;
16590 svn_sqlite__stmt_t *stmt;
16591 svn_boolean_t have_row;
16593 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
16595 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &wri_relpath, db,
16596 wri_abspath, scratch_pool,
16598 VERIFY_USABLE_WCROOT(wcroot);
16600 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
16601 STMT_FIND_REPOS_PATH_IN_WC));
16602 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, repos_relpath));
16603 SVN_ERR(svn_sqlite__step(&have_row, stmt));
16605 *local_abspath_list = apr_array_make(result_pool, have_row ? 1 : 0,
16606 sizeof(const char*));
16609 const char *local_relpath;
16610 const char *local_abspath;
16612 local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
16613 local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
16615 APR_ARRAY_PUSH(*local_abspath_list, const char *) = local_abspath;
16617 SVN_ERR(svn_sqlite__step(&have_row, stmt));
16620 return svn_error_trace(svn_sqlite__reset(stmt));