2 * wc_db.c : manipulating the administrative database
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
13 * http://www.apache.org/licenses/LICENSE-2.0
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
21 * ====================================================================
24 #define SVN_WC__I_AM_WC_DB
27 #include <apr_pools.h>
30 #include "svn_types.h"
31 #include "svn_error.h"
32 #include "svn_dirent_uri.h"
35 #include "svn_sorts.h"
37 #include "svn_checksum.h"
38 #include "svn_pools.h"
42 #include "adm_files.h"
43 #include "wc-queries.h"
46 #include "conflicts.h"
47 #include "wc_db_private.h"
48 #include "workqueue.h"
49 #include "token-map.h"
51 #include "svn_private_config.h"
52 #include "private/svn_sqlite.h"
53 #include "private/svn_skel.h"
54 #include "private/svn_wc_private.h"
55 #include "private/svn_token.h"
58 #define NOT_IMPLEMENTED() SVN__NOT_IMPLEMENTED()
62 * Some filename constants.
64 #define SDB_FILE "wc.db"
66 #define WCROOT_TEMPDIR_RELPATH "tmp"
70 * PARAMETER ASSERTIONS
72 * Every (semi-)public entrypoint in this file has a set of assertions on
73 * the parameters passed into the function. Since this is a brand new API,
74 * we want to make sure that everybody calls it properly. The original WC
75 * code had years to catch stray bugs, but we do not have that luxury in
76 * the wc-nb rewrite. Any extra assurances that we can find will be
77 * welcome. The asserts will ensure we have no doubt about the values
78 * passed into the function.
80 * Some parameters are *not* specifically asserted. Typically, these are
81 * params that will be used immediately, so something like a NULL value
84 * ### near 1.7 release, it would be a Good Thing to review the assertions
85 * ### and decide if any can be removed or switched to assert() in order
86 * ### to remove their runtime cost in the production release.
91 * Each function should leave the database in a consistent state. If it
92 * does *not*, then the implication is some other function needs to be
93 * called to restore consistency. Subtle requirements like that are hard
94 * to maintain over a long period of time, so this API will not allow it.
97 * STANDARD VARIABLE NAMES
99 * db working copy database (this module)
100 * sdb SQLite database (not to be confused with 'db')
101 * wc_id a WCROOT id associated with a node
104 #define INVALID_REPOS_ID ((apr_int64_t) -1)
105 #define UNKNOWN_WC_ID ((apr_int64_t) -1)
106 #define FORMAT_FROM_SDB (-1)
108 /* Check if column number I, a property-skel column, contains a non-empty
109 set of properties. The empty set of properties is stored as "()", so we
110 have properties if the size of the column is larger than 2. */
111 #define SQLITE_PROPERTIES_AVAILABLE(stmt, i) \
112 (svn_sqlite__column_bytes(stmt, i) > 2)
115 svn_wc__db_op_depth_for_upgrade(const char *local_relpath)
117 return relpath_depth(local_relpath);
121 /* Representation of a new base row for the NODES table */
122 typedef struct insert_base_baton_t {
123 /* common to all insertions into BASE */
124 svn_wc__db_status_t status;
125 svn_node_kind_t kind;
126 apr_int64_t repos_id;
127 const char *repos_relpath;
128 svn_revnum_t revision;
130 /* Only used when repos_id == INVALID_REPOS_ID */
131 const char *repos_root_url;
132 const char *repos_uuid;
134 /* common to all "normal" presence insertions */
135 const apr_hash_t *props;
136 svn_revnum_t changed_rev;
137 apr_time_t changed_date;
138 const char *changed_author;
139 const apr_hash_t *dav_cache;
141 /* for inserting directories */
142 const apr_array_header_t *children;
145 /* for inserting files */
146 const svn_checksum_t *checksum;
148 /* for inserting symlinks */
151 svn_boolean_t file_external;
153 /* may need to insert/update ACTUAL to record a conflict */
154 const svn_skel_t *conflict;
156 /* may need to insert/update ACTUAL to record new properties */
157 svn_boolean_t update_actual_props;
158 const apr_hash_t *new_actual_props;
160 /* A depth-first ordered array of svn_prop_inherited_item_t *
161 structures representing the properties inherited by the base
163 apr_array_header_t *iprops;
165 /* maybe we should copy information from a previous record? */
166 svn_boolean_t keep_recorded_info;
168 /* insert a base-deleted working node as well as a base node */
169 svn_boolean_t insert_base_deleted;
171 /* delete the current working nodes above BASE */
172 svn_boolean_t delete_working;
174 /* may have work items to queue in this transaction */
175 const svn_skel_t *work_items;
177 } insert_base_baton_t;
180 /* Representation of a new working row for the NODES table */
181 typedef struct insert_working_baton_t {
182 /* common to all insertions into WORKING (including NODE_DATA) */
183 svn_wc__db_status_t presence;
184 svn_node_kind_t kind;
187 /* common to all "normal" presence insertions */
188 const apr_hash_t *props;
189 svn_revnum_t changed_rev;
190 apr_time_t changed_date;
191 const char *changed_author;
192 apr_int64_t original_repos_id;
193 const char *original_repos_relpath;
194 svn_revnum_t original_revnum;
195 svn_boolean_t moved_here;
197 /* for inserting directories */
198 const apr_array_header_t *children;
201 /* for inserting (copied/moved-here) files */
202 const svn_checksum_t *checksum;
204 /* for inserting symlinks */
207 svn_boolean_t update_actual_props;
208 const apr_hash_t *new_actual_props;
210 /* may have work items to queue in this transaction */
211 const svn_skel_t *work_items;
213 /* may have conflict to install in this transaction */
214 const svn_skel_t *conflict;
216 /* If the value is > 0 and < op_depth, also insert a not-present
217 at op-depth NOT_PRESENT_OP_DEPTH, based on this same information */
218 int not_present_op_depth;
220 } insert_working_baton_t;
222 /* Representation of a new row for the EXTERNALS table */
223 typedef struct insert_external_baton_t {
224 /* common to all insertions into EXTERNALS */
225 svn_node_kind_t kind;
226 svn_wc__db_status_t presence;
228 /* The repository of the external */
229 apr_int64_t repos_id;
230 /* for file and symlink externals */
231 const char *repos_relpath;
232 svn_revnum_t revision;
234 /* Only used when repos_id == INVALID_REPOS_ID */
235 const char *repos_root_url;
236 const char *repos_uuid;
238 /* for file and symlink externals */
239 const apr_hash_t *props;
240 apr_array_header_t *iprops;
241 svn_revnum_t changed_rev;
242 apr_time_t changed_date;
243 const char *changed_author;
244 const apr_hash_t *dav_cache;
246 /* for inserting files */
247 const svn_checksum_t *checksum;
249 /* for inserting symlinks */
252 const char *record_ancestor_relpath;
253 const char *recorded_repos_relpath;
254 svn_revnum_t recorded_peg_revision;
255 svn_revnum_t recorded_revision;
257 /* may need to insert/update ACTUAL to record a conflict */
258 const svn_skel_t *conflict;
260 /* may need to insert/update ACTUAL to record new properties */
261 svn_boolean_t update_actual_props;
262 const apr_hash_t *new_actual_props;
264 /* maybe we should copy information from a previous record? */
265 svn_boolean_t keep_recorded_info;
267 /* may have work items to queue in this transaction */
268 const svn_skel_t *work_items;
270 } insert_external_baton_t;
273 /* Forward declarations */
275 add_work_items(svn_sqlite__db_t *sdb,
276 const svn_skel_t *skel,
277 apr_pool_t *scratch_pool);
280 set_actual_props(apr_int64_t wc_id,
281 const char *local_relpath,
283 svn_sqlite__db_t *db,
284 apr_pool_t *scratch_pool);
287 insert_incomplete_children(svn_sqlite__db_t *sdb,
289 const char *local_relpath,
290 apr_int64_t repos_id,
291 const char *repos_relpath,
292 svn_revnum_t revision,
293 const apr_array_header_t *children,
295 apr_pool_t *scratch_pool);
298 db_read_pristine_props(apr_hash_t **props,
299 svn_wc__db_wcroot_t *wcroot,
300 const char *local_relpath,
301 svn_boolean_t deleted_ok,
302 apr_pool_t *result_pool,
303 apr_pool_t *scratch_pool);
306 read_info(svn_wc__db_status_t *status,
307 svn_node_kind_t *kind,
308 svn_revnum_t *revision,
309 const char **repos_relpath,
310 apr_int64_t *repos_id,
311 svn_revnum_t *changed_rev,
312 apr_time_t *changed_date,
313 const char **changed_author,
315 const svn_checksum_t **checksum,
317 const char **original_repos_relpath,
318 apr_int64_t *original_repos_id,
319 svn_revnum_t *original_revision,
320 svn_wc__db_lock_t **lock,
321 svn_filesize_t *recorded_size,
322 apr_time_t *recorded_time,
323 const char **changelist,
324 svn_boolean_t *conflicted,
325 svn_boolean_t *op_root,
326 svn_boolean_t *had_props,
327 svn_boolean_t *props_mod,
328 svn_boolean_t *have_base,
329 svn_boolean_t *have_more_work,
330 svn_boolean_t *have_work,
331 svn_wc__db_wcroot_t *wcroot,
332 const char *local_relpath,
333 apr_pool_t *result_pool,
334 apr_pool_t *scratch_pool);
337 scan_addition(svn_wc__db_status_t *status,
338 const char **op_root_relpath,
339 const char **repos_relpath,
340 apr_int64_t *repos_id,
341 const char **original_repos_relpath,
342 apr_int64_t *original_repos_id,
343 svn_revnum_t *original_revision,
344 const char **moved_from_relpath,
345 const char **moved_from_op_root_relpath,
346 int *moved_from_op_depth,
347 svn_wc__db_wcroot_t *wcroot,
348 const char *local_relpath,
349 apr_pool_t *result_pool,
350 apr_pool_t *scratch_pool);
353 convert_to_working_status(svn_wc__db_status_t *working_status,
354 svn_wc__db_status_t status);
357 wclock_owns_lock(svn_boolean_t *own_lock,
358 svn_wc__db_wcroot_t *wcroot,
359 const char *local_relpath,
361 apr_pool_t *scratch_pool);
364 db_is_switched(svn_boolean_t *is_switched,
365 svn_node_kind_t *kind,
366 svn_wc__db_wcroot_t *wcroot,
367 const char *local_relpath,
368 apr_pool_t *scratch_pool);
371 /* Return the absolute path, in local path style, of LOCAL_RELPATH
374 path_for_error_message(const svn_wc__db_wcroot_t *wcroot,
375 const char *local_relpath,
376 apr_pool_t *result_pool)
378 const char *local_abspath
379 = svn_dirent_join(wcroot->abspath, local_relpath, result_pool);
381 return svn_dirent_local_style(local_abspath, result_pool);
385 /* Return a file size from column SLOT of the SQLITE statement STMT, or
386 SVN_INVALID_FILESIZE if the column value is NULL. */
387 static svn_filesize_t
388 get_recorded_size(svn_sqlite__stmt_t *stmt, int slot)
390 if (svn_sqlite__column_is_null(stmt, slot))
391 return SVN_INVALID_FILESIZE;
392 return svn_sqlite__column_int64(stmt, slot);
396 /* Return a lock info structure constructed from the given columns of the
397 SQLITE statement STMT, or return NULL if the token column value is null. */
398 static svn_wc__db_lock_t *
399 lock_from_columns(svn_sqlite__stmt_t *stmt,
404 apr_pool_t *result_pool)
406 svn_wc__db_lock_t *lock;
408 if (svn_sqlite__column_is_null(stmt, col_token))
414 lock = apr_pcalloc(result_pool, sizeof(svn_wc__db_lock_t));
415 lock->token = svn_sqlite__column_text(stmt, col_token, result_pool);
416 lock->owner = svn_sqlite__column_text(stmt, col_owner, result_pool);
417 lock->comment = svn_sqlite__column_text(stmt, col_comment, result_pool);
418 lock->date = svn_sqlite__column_int64(stmt, col_date);
425 svn_wc__db_fetch_repos_info(const char **repos_root_url,
426 const char **repos_uuid,
427 svn_sqlite__db_t *sdb,
428 apr_int64_t repos_id,
429 apr_pool_t *result_pool)
431 svn_sqlite__stmt_t *stmt;
432 svn_boolean_t have_row;
434 if (!repos_root_url && !repos_uuid)
437 if (repos_id == INVALID_REPOS_ID)
440 *repos_root_url = NULL;
446 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
447 STMT_SELECT_REPOSITORY_BY_ID));
448 SVN_ERR(svn_sqlite__bindf(stmt, "i", repos_id));
449 SVN_ERR(svn_sqlite__step(&have_row, stmt));
451 return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
452 _("No REPOSITORY table entry for id '%ld'"),
456 *repos_root_url = svn_sqlite__column_text(stmt, 0, result_pool);
458 *repos_uuid = svn_sqlite__column_text(stmt, 1, result_pool);
460 return svn_error_trace(svn_sqlite__reset(stmt));
463 /* Set *REPOS_ID, *REVISION and *REPOS_RELPATH from the given columns of the
464 SQLITE statement STMT, or to NULL/SVN_INVALID_REVNUM if the respective
465 column value is null. Any of the output parameters may be NULL if not
468 repos_location_from_columns(apr_int64_t *repos_id,
469 svn_revnum_t *revision,
470 const char **repos_relpath,
471 svn_sqlite__stmt_t *stmt,
474 int col_repos_relpath,
475 apr_pool_t *result_pool)
479 /* Fetch repository information via REPOS_ID. */
480 if (svn_sqlite__column_is_null(stmt, col_repos_id))
481 *repos_id = INVALID_REPOS_ID;
483 *repos_id = svn_sqlite__column_int64(stmt, col_repos_id);
487 *revision = svn_sqlite__column_revnum(stmt, col_revision);
491 *repos_relpath = svn_sqlite__column_text(stmt, col_repos_relpath,
497 /* Get the statement given by STMT_IDX, and bind the appropriate wc_id and
498 local_relpath based upon LOCAL_ABSPATH. Store it in *STMT, and use
499 SCRATCH_POOL for temporary allocations.
501 Note: WC_ID and LOCAL_RELPATH must be arguments 1 and 2 in the statement. */
503 get_statement_for_path(svn_sqlite__stmt_t **stmt,
505 const char *local_abspath,
507 apr_pool_t *scratch_pool)
509 svn_wc__db_wcroot_t *wcroot;
510 const char *local_relpath;
512 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
514 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
515 local_abspath, scratch_pool, scratch_pool));
516 VERIFY_USABLE_WCROOT(wcroot);
518 SVN_ERR(svn_sqlite__get_statement(stmt, wcroot->sdb, stmt_idx));
519 SVN_ERR(svn_sqlite__bindf(*stmt, "is", wcroot->wc_id, local_relpath));
525 /* For a given REPOS_ROOT_URL/REPOS_UUID pair, return the existing REPOS_ID
526 value. If one does not exist, then create a new one. */
528 create_repos_id(apr_int64_t *repos_id,
529 const char *repos_root_url,
530 const char *repos_uuid,
531 svn_sqlite__db_t *sdb,
532 apr_pool_t *scratch_pool)
534 svn_sqlite__stmt_t *get_stmt;
535 svn_sqlite__stmt_t *insert_stmt;
536 svn_boolean_t have_row;
538 SVN_ERR(svn_sqlite__get_statement(&get_stmt, sdb, STMT_SELECT_REPOSITORY));
539 SVN_ERR(svn_sqlite__bindf(get_stmt, "s", repos_root_url));
540 SVN_ERR(svn_sqlite__step(&have_row, get_stmt));
544 *repos_id = svn_sqlite__column_int64(get_stmt, 0);
545 return svn_error_trace(svn_sqlite__reset(get_stmt));
547 SVN_ERR(svn_sqlite__reset(get_stmt));
549 /* NOTE: strictly speaking, there is a race condition between the
550 above query and the insertion below. We're simply going to ignore
551 that, as it means two processes are *modifying* the working copy
552 at the same time, *and* new repositores are becoming visible.
553 This is rare enough, let alone the miniscule chance of hitting
554 this race condition. Further, simply failing out will leave the
555 database in a consistent state, and the user can just re-run the
558 SVN_ERR(svn_sqlite__get_statement(&insert_stmt, sdb,
559 STMT_INSERT_REPOSITORY));
560 SVN_ERR(svn_sqlite__bindf(insert_stmt, "ss", repos_root_url, repos_uuid));
561 return svn_error_trace(svn_sqlite__insert(repos_id, insert_stmt));
565 /* Initialize the baton with appropriate "blank" values. This allows the
566 insertion function to leave certain columns null. */
568 blank_ibb(insert_base_baton_t *pibb)
570 memset(pibb, 0, sizeof(*pibb));
571 pibb->revision = SVN_INVALID_REVNUM;
572 pibb->changed_rev = SVN_INVALID_REVNUM;
573 pibb->depth = svn_depth_infinity;
574 pibb->repos_id = INVALID_REPOS_ID;
579 svn_wc__db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot,
580 const char *local_relpath,
581 svn_node_kind_t kind,
583 apr_pool_t *scratch_pool)
585 svn_boolean_t have_row;
586 svn_sqlite__stmt_t *stmt;
588 const char *parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
590 SVN_ERR_ASSERT(local_relpath[0]);
592 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
593 STMT_SELECT_LOWEST_WORKING_NODE));
594 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, parent_relpath,
596 SVN_ERR(svn_sqlite__step(&have_row, stmt));
598 parent_op_depth = svn_sqlite__column_int(stmt, 0);
599 SVN_ERR(svn_sqlite__reset(stmt));
602 int existing_op_depth;
604 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
606 SVN_ERR(svn_sqlite__step(&have_row, stmt));
608 existing_op_depth = svn_sqlite__column_int(stmt, 0);
609 SVN_ERR(svn_sqlite__reset(stmt));
610 if (!have_row || parent_op_depth < existing_op_depth)
612 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
613 STMT_INSTALL_WORKING_NODE_FOR_DELETE));
614 SVN_ERR(svn_sqlite__bindf(stmt, "isdst", wcroot->wc_id,
615 local_relpath, parent_op_depth,
616 parent_relpath, kind_map, kind));
617 SVN_ERR(svn_sqlite__update(NULL, stmt));
625 /* This is the reverse of svn_wc__db_extend_parent_delete.
627 When removing a node if the parent has a higher working node then
628 the parent node and this node are both deleted or replaced and any
629 delete over this node must be removed.
632 svn_wc__db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot,
633 const char *local_relpath,
635 apr_pool_t *scratch_pool)
637 svn_sqlite__stmt_t *stmt;
639 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
640 STMT_DELETE_LOWEST_WORKING_NODE));
641 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
643 SVN_ERR(svn_sqlite__step_done(stmt));
650 /* Insert the base row represented by (insert_base_baton_t *) BATON. */
652 insert_base_node(const insert_base_baton_t *pibb,
653 svn_wc__db_wcroot_t *wcroot,
654 const char *local_relpath,
655 apr_pool_t *scratch_pool)
657 apr_int64_t repos_id = pibb->repos_id;
658 svn_sqlite__stmt_t *stmt;
659 svn_filesize_t recorded_size = SVN_INVALID_FILESIZE;
660 apr_int64_t recorded_time;
662 /* The directory at the WCROOT has a NULL parent_relpath. Otherwise,
663 bind the appropriate parent_relpath. */
664 const char *parent_relpath =
665 (*local_relpath == '\0') ? NULL
666 : svn_relpath_dirname(local_relpath, scratch_pool);
668 if (pibb->repos_id == INVALID_REPOS_ID)
669 SVN_ERR(create_repos_id(&repos_id, pibb->repos_root_url, pibb->repos_uuid,
670 wcroot->sdb, scratch_pool));
672 SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
673 SVN_ERR_ASSERT(pibb->repos_relpath != NULL);
675 if (pibb->keep_recorded_info)
677 svn_boolean_t have_row;
678 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
679 STMT_SELECT_BASE_NODE));
680 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
681 SVN_ERR(svn_sqlite__step(&have_row, stmt));
684 /* Preserve size and modification time if caller asked us to. */
685 recorded_size = get_recorded_size(stmt, 6);
686 recorded_time = svn_sqlite__column_int64(stmt, 12);
688 SVN_ERR(svn_sqlite__reset(stmt));
691 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
692 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisr"
694 "isnnnnns", /* 12 - 19 */
695 wcroot->wc_id, /* 1 */
696 local_relpath, /* 2 */
697 0, /* op_depth is 0 for base */
698 parent_relpath, /* 4 */
702 presence_map, pibb->status, /* 8 */
703 (pibb->kind == svn_node_dir) ? /* 9 */
704 svn_token__to_word(depth_map, pibb->depth) : NULL,
705 kind_map, pibb->kind, /* 10 */
706 pibb->changed_rev, /* 11 */
707 pibb->changed_date, /* 12 */
708 pibb->changed_author, /* 13 */
709 (pibb->kind == svn_node_symlink) ?
710 pibb->target : NULL)); /* 19 */
711 if (pibb->kind == svn_node_file)
714 && pibb->status != svn_wc__db_status_not_present
715 && pibb->status != svn_wc__db_status_excluded
716 && pibb->status != svn_wc__db_status_server_excluded)
717 return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
718 _("The file '%s' has no checksum."),
719 path_for_error_message(wcroot, local_relpath,
722 SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, pibb->checksum,
725 if (recorded_size != SVN_INVALID_FILESIZE)
727 SVN_ERR(svn_sqlite__bind_int64(stmt, 16, recorded_size));
728 SVN_ERR(svn_sqlite__bind_int64(stmt, 17, recorded_time));
732 /* Set properties. Must be null if presence not normal or incomplete. */
733 assert(pibb->status == svn_wc__db_status_normal
734 || pibb->status == svn_wc__db_status_incomplete
735 || pibb->props == NULL);
736 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, pibb->props,
739 SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, pibb->iprops,
743 SVN_ERR(svn_sqlite__bind_properties(stmt, 18, pibb->dav_cache,
746 if (pibb->file_external)
747 SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
749 SVN_ERR(svn_sqlite__insert(NULL, stmt));
751 if (pibb->update_actual_props)
753 /* Cast away const, to allow calling property helpers */
754 apr_hash_t *base_props = (apr_hash_t *)pibb->props;
755 apr_hash_t *new_actual_props = (apr_hash_t *)pibb->new_actual_props;
757 if (base_props != NULL
758 && new_actual_props != NULL
759 && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
761 apr_array_header_t *diffs;
763 SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
766 if (diffs->nelts == 0)
767 new_actual_props = NULL;
770 SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props,
771 wcroot->sdb, scratch_pool));
774 if (pibb->kind == svn_node_dir && pibb->children)
775 SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
784 /* When this is not the root node, check shadowing behavior */
788 && ((pibb->status == svn_wc__db_status_normal)
789 || (pibb->status == svn_wc__db_status_incomplete))
790 && ! pibb->file_external)
792 SVN_ERR(svn_wc__db_extend_parent_delete(wcroot, local_relpath,
796 else if (pibb->status == svn_wc__db_status_not_present
797 || pibb->status == svn_wc__db_status_server_excluded
798 || pibb->status == svn_wc__db_status_excluded)
800 SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
805 if (pibb->delete_working)
807 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
808 STMT_DELETE_WORKING_NODE));
809 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
810 SVN_ERR(svn_sqlite__step_done(stmt));
812 if (pibb->insert_base_deleted)
814 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
815 STMT_INSERT_DELETE_FROM_BASE));
816 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
817 wcroot->wc_id, local_relpath,
818 relpath_depth(local_relpath)));
819 SVN_ERR(svn_sqlite__step_done(stmt));
822 SVN_ERR(add_work_items(wcroot->sdb, pibb->work_items, scratch_pool));
824 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
825 pibb->conflict, scratch_pool));
831 /* Initialize the baton with appropriate "blank" values. This allows the
832 insertion function to leave certain columns null. */
834 blank_iwb(insert_working_baton_t *piwb)
836 memset(piwb, 0, sizeof(*piwb));
837 piwb->changed_rev = SVN_INVALID_REVNUM;
838 piwb->depth = svn_depth_infinity;
840 /* ORIGINAL_REPOS_ID and ORIGINAL_REVNUM could use some kind of "nil"
841 value, but... meh. We'll avoid them if ORIGINAL_REPOS_RELPATH==NULL. */
845 /* Insert a row in NODES for each (const char *) child name in CHILDREN,
846 whose parent directory is LOCAL_RELPATH, at op_depth=OP_DEPTH. Set each
847 child's presence to 'incomplete', kind to 'unknown', repos_id to REPOS_ID,
848 repos_path by appending the child name to REPOS_PATH, and revision to
849 REVISION (which should match the parent's revision).
851 If REPOS_ID is INVALID_REPOS_ID, set each child's repos_id to null. */
853 insert_incomplete_children(svn_sqlite__db_t *sdb,
855 const char *local_relpath,
856 apr_int64_t repos_id,
857 const char *repos_path,
858 svn_revnum_t revision,
859 const apr_array_header_t *children,
861 apr_pool_t *scratch_pool)
863 svn_sqlite__stmt_t *stmt;
865 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
866 apr_hash_t *moved_to_relpaths = apr_hash_make(scratch_pool);
868 SVN_ERR_ASSERT(repos_path != NULL || op_depth > 0);
869 SVN_ERR_ASSERT((repos_id != INVALID_REPOS_ID)
870 == (repos_path != NULL));
872 /* If we're inserting WORKING nodes, we might be replacing existing
873 * nodes which were moved-away. We need to retain the moved-to relpath of
874 * such nodes in order not to lose move information during replace. */
877 for (i = children->nelts; i--; )
879 const char *name = APR_ARRAY_IDX(children, i, const char *);
880 svn_boolean_t have_row;
882 svn_pool_clear(iterpool);
884 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
885 STMT_SELECT_WORKING_NODE));
886 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id,
887 svn_relpath_join(local_relpath, name,
889 SVN_ERR(svn_sqlite__step(&have_row, stmt));
890 if (have_row && !svn_sqlite__column_is_null(stmt, 14))
891 svn_hash_sets(moved_to_relpaths, name,
892 svn_sqlite__column_text(stmt, 14, scratch_pool));
894 SVN_ERR(svn_sqlite__reset(stmt));
898 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
900 for (i = children->nelts; i--; )
902 const char *name = APR_ARRAY_IDX(children, i, const char *);
904 svn_pool_clear(iterpool);
906 SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnrsnsnnnnnnnnnnsn",
908 svn_relpath_join(local_relpath, name,
913 "incomplete", /* 8, presence */
914 "unknown", /* 10, kind */
916 svn_hash_gets(moved_to_relpaths, name)));
917 if (repos_id != INVALID_REPOS_ID)
919 SVN_ERR(svn_sqlite__bind_int64(stmt, 5, repos_id));
920 SVN_ERR(svn_sqlite__bind_text(stmt, 6,
921 svn_relpath_join(repos_path, name,
925 SVN_ERR(svn_sqlite__insert(NULL, stmt));
928 svn_pool_destroy(iterpool);
934 /* Insert the working row represented by (insert_working_baton_t *) BATON. */
936 insert_working_node(const insert_working_baton_t *piwb,
937 svn_wc__db_wcroot_t *wcroot,
938 const char *local_relpath,
939 apr_pool_t *scratch_pool)
941 const char *parent_relpath;
942 const char *moved_to_relpath = NULL;
943 svn_sqlite__stmt_t *stmt;
944 svn_boolean_t have_row;
946 SVN_ERR_ASSERT(piwb->op_depth > 0);
948 /* We cannot insert a WORKING_NODE row at the wcroot. */
949 SVN_ERR_ASSERT(*local_relpath != '\0');
950 parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
952 /* Preserve existing moved-to information for this relpath,
953 * which might exist in case we're replacing an existing base-deleted
955 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
956 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
958 SVN_ERR(svn_sqlite__step(&have_row, stmt));
960 moved_to_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
961 SVN_ERR(svn_sqlite__reset(stmt));
963 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
964 SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnntstrisn"
965 "nnnn" /* properties translated_size last_mod_time dav_cache */
966 "sns", /* symlink_target, file_external, moved_to */
967 wcroot->wc_id, local_relpath,
970 presence_map, piwb->presence,
971 (piwb->kind == svn_node_dir)
972 ? svn_token__to_word(depth_map, piwb->depth) : NULL,
973 kind_map, piwb->kind,
976 piwb->changed_author,
977 /* Note: incomplete nodes may have a NULL target. */
978 (piwb->kind == svn_node_symlink)
979 ? piwb->target : NULL,
982 if (piwb->moved_here)
984 SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
987 if (piwb->kind == svn_node_file)
989 SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, piwb->checksum,
993 if (piwb->original_repos_relpath != NULL)
995 SVN_ERR(svn_sqlite__bind_int64(stmt, 5, piwb->original_repos_id));
996 SVN_ERR(svn_sqlite__bind_text(stmt, 6, piwb->original_repos_relpath));
997 SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, piwb->original_revnum));
1000 /* Set properties. Must be null if presence not normal or incomplete. */
1001 assert(piwb->presence == svn_wc__db_status_normal
1002 || piwb->presence == svn_wc__db_status_incomplete
1003 || piwb->props == NULL);
1004 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, piwb->props, scratch_pool));
1006 SVN_ERR(svn_sqlite__insert(NULL, stmt));
1008 /* Insert incomplete children, if specified.
1009 The children are part of the same op and so have the same op_depth.
1010 (The only time we'd want a different depth is during a recursive
1011 simple add, but we never insert children here during a simple add.) */
1012 if (piwb->kind == svn_node_dir && piwb->children)
1013 SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
1015 INVALID_REPOS_ID /* inherit repos_id */,
1016 NULL /* inherit repos_path */,
1017 piwb->original_revnum,
1022 if (piwb->update_actual_props)
1024 /* Cast away const, to allow calling property helpers */
1025 apr_hash_t *base_props = (apr_hash_t *)piwb->props;
1026 apr_hash_t *new_actual_props = (apr_hash_t *)piwb->new_actual_props;
1028 if (base_props != NULL
1029 && new_actual_props != NULL
1030 && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
1032 apr_array_header_t *diffs;
1034 SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
1037 if (diffs->nelts == 0)
1038 new_actual_props = NULL;
1041 SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props,
1042 wcroot->sdb, scratch_pool));
1045 if (piwb->kind == svn_node_dir)
1047 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1048 STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST));
1049 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1050 SVN_ERR(svn_sqlite__step_done(stmt));
1052 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1053 STMT_DELETE_ACTUAL_EMPTY));
1054 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1055 SVN_ERR(svn_sqlite__step_done(stmt));
1058 if (piwb->not_present_op_depth > 0
1059 && piwb->not_present_op_depth < piwb->op_depth)
1061 /* And also insert a not-present node to tell the commit processing that
1062 a child of the parent node was not copied. */
1063 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1066 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
1067 wcroot->wc_id, local_relpath,
1068 piwb->not_present_op_depth, parent_relpath,
1069 piwb->original_repos_id,
1070 piwb->original_repos_relpath,
1071 piwb->original_revnum,
1072 presence_map, svn_wc__db_status_not_present,
1074 kind_map, piwb->kind));
1076 SVN_ERR(svn_sqlite__step_done(stmt));
1079 SVN_ERR(add_work_items(wcroot->sdb, piwb->work_items, scratch_pool));
1081 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
1082 piwb->conflict, scratch_pool));
1084 return SVN_NO_ERROR;
1088 /* Each name is allocated in RESULT_POOL and stored into CHILDREN as a key
1089 pointed to the same name. */
1090 static svn_error_t *
1091 add_children_to_hash(apr_hash_t *children,
1093 svn_sqlite__db_t *sdb,
1095 const char *parent_relpath,
1096 apr_pool_t *result_pool)
1098 svn_sqlite__stmt_t *stmt;
1099 svn_boolean_t have_row;
1101 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, stmt_idx));
1102 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, parent_relpath));
1103 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1106 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1107 const char *name = svn_relpath_basename(child_relpath, result_pool);
1109 svn_hash_sets(children, name, name);
1111 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1114 return svn_sqlite__reset(stmt);
1118 /* Set *CHILDREN to a new array of the (const char *) basenames of the
1119 immediate children, whatever their status, of the working node at
1121 static svn_error_t *
1122 gather_children2(const apr_array_header_t **children,
1123 svn_wc__db_wcroot_t *wcroot,
1124 const char *local_relpath,
1125 apr_pool_t *result_pool,
1126 apr_pool_t *scratch_pool)
1128 apr_hash_t *names_hash = apr_hash_make(scratch_pool);
1129 apr_array_header_t *names_array;
1131 /* All of the names get allocated in RESULT_POOL. It
1132 appears to be faster to use the hash to remove duplicates than to
1133 use DISTINCT in the SQL query. */
1134 SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_WORKING_CHILDREN,
1135 wcroot->sdb, wcroot->wc_id,
1136 local_relpath, result_pool));
1138 SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool));
1139 *children = names_array;
1140 return SVN_NO_ERROR;
1143 /* Return in *CHILDREN all of the children of the directory LOCAL_RELPATH,
1144 of any status, in all op-depths in the NODES table. */
1145 static svn_error_t *
1146 gather_children(const apr_array_header_t **children,
1147 svn_wc__db_wcroot_t *wcroot,
1148 const char *local_relpath,
1149 apr_pool_t *result_pool,
1150 apr_pool_t *scratch_pool)
1152 apr_hash_t *names_hash = apr_hash_make(scratch_pool);
1153 apr_array_header_t *names_array;
1155 /* All of the names get allocated in RESULT_POOL. It
1156 appears to be faster to use the hash to remove duplicates than to
1157 use DISTINCT in the SQL query. */
1158 SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_NODE_CHILDREN,
1159 wcroot->sdb, wcroot->wc_id,
1160 local_relpath, result_pool));
1162 SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool));
1163 *children = names_array;
1164 return SVN_NO_ERROR;
1168 /* Set *CHILDREN to a new array of (const char *) names of the children of
1169 the repository directory corresponding to WCROOT:LOCAL_RELPATH:OP_DEPTH -
1170 that is, only the children that are at the same op-depth as their parent. */
1171 static svn_error_t *
1172 gather_repo_children(const apr_array_header_t **children,
1173 svn_wc__db_wcroot_t *wcroot,
1174 const char *local_relpath,
1176 apr_pool_t *result_pool,
1177 apr_pool_t *scratch_pool)
1179 apr_array_header_t *result
1180 = apr_array_make(result_pool, 0, sizeof(const char *));
1181 svn_sqlite__stmt_t *stmt;
1182 svn_boolean_t have_row;
1184 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1185 STMT_SELECT_OP_DEPTH_CHILDREN));
1186 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1188 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1191 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1193 /* Allocate the name in RESULT_POOL so we won't have to copy it. */
1194 APR_ARRAY_PUSH(result, const char *)
1195 = svn_relpath_basename(child_relpath, result_pool);
1197 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1199 SVN_ERR(svn_sqlite__reset(stmt));
1202 return SVN_NO_ERROR;
1206 svn_wc__db_get_children_op_depth(apr_hash_t **children,
1207 svn_wc__db_wcroot_t *wcroot,
1208 const char *local_relpath,
1210 apr_pool_t *result_pool,
1211 apr_pool_t *scratch_pool)
1213 svn_sqlite__stmt_t *stmt;
1214 svn_boolean_t have_row;
1216 *children = apr_hash_make(result_pool);
1218 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1219 STMT_SELECT_OP_DEPTH_CHILDREN));
1220 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1222 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1225 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1226 svn_node_kind_t *child_kind = apr_palloc(result_pool, sizeof(svn_node_kind_t));
1228 *child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
1229 svn_hash_sets(*children,
1230 svn_relpath_basename(child_relpath, result_pool),
1233 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1235 SVN_ERR(svn_sqlite__reset(stmt));
1237 return SVN_NO_ERROR;
1241 /* Return TRUE if CHILD_ABSPATH is an immediate child of PARENT_ABSPATH.
1242 * Else, return FALSE. */
1243 static svn_boolean_t
1244 is_immediate_child_path(const char *parent_abspath, const char *child_abspath)
1246 const char *local_relpath = svn_dirent_skip_ancestor(parent_abspath,
1249 /* To be an immediate child local_relpath should have one (not empty)
1251 return local_relpath && *local_relpath && !strchr(local_relpath, '/');
1255 /* Remove the access baton for LOCAL_ABSPATH from ACCESS_CACHE. */
1257 remove_from_access_cache(apr_hash_t *access_cache,
1258 const char *local_abspath)
1260 svn_wc_adm_access_t *adm_access;
1262 adm_access = svn_hash_gets(access_cache, local_abspath);
1264 svn_wc__adm_access_set_entries(adm_access, NULL);
1268 /* Flush the access baton for LOCAL_ABSPATH, and any of its children up to
1269 * the specified DEPTH, from the access baton cache in WCROOT.
1270 * Also flush the access baton for the parent of LOCAL_ABSPATH.I
1272 * This function must be called when the access baton cache goes stale,
1273 * i.e. data about LOCAL_ABSPATH will need to be read again from disk.
1275 * Use SCRATCH_POOL for temporary allocations. */
1276 static svn_error_t *
1277 flush_entries(svn_wc__db_wcroot_t *wcroot,
1278 const char *local_abspath,
1280 apr_pool_t *scratch_pool)
1282 const char *parent_abspath;
1284 if (apr_hash_count(wcroot->access_cache) == 0)
1285 return SVN_NO_ERROR;
1287 remove_from_access_cache(wcroot->access_cache, local_abspath);
1289 if (depth > svn_depth_empty)
1291 apr_hash_index_t *hi;
1293 /* Flush access batons of children within the specified depth. */
1294 for (hi = apr_hash_first(scratch_pool, wcroot->access_cache);
1296 hi = apr_hash_next(hi))
1298 const char *item_abspath = svn__apr_hash_index_key(hi);
1300 if ((depth == svn_depth_files || depth == svn_depth_immediates) &&
1301 is_immediate_child_path(local_abspath, item_abspath))
1303 remove_from_access_cache(wcroot->access_cache, item_abspath);
1305 else if (depth == svn_depth_infinity &&
1306 svn_dirent_is_ancestor(local_abspath, item_abspath))
1308 remove_from_access_cache(wcroot->access_cache, item_abspath);
1313 /* We're going to be overly aggressive here and just flush the parent
1314 without doing much checking. This may hurt performance for
1315 legacy API consumers, but that's not our problem. :) */
1316 parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
1317 remove_from_access_cache(wcroot->access_cache, parent_abspath);
1319 return SVN_NO_ERROR;
1323 /* Add a single WORK_ITEM into the given SDB's WORK_QUEUE table. This does
1324 not perform its work within a transaction, assuming the caller will
1326 static svn_error_t *
1327 add_single_work_item(svn_sqlite__db_t *sdb,
1328 const svn_skel_t *work_item,
1329 apr_pool_t *scratch_pool)
1331 svn_stringbuf_t *serialized;
1332 svn_sqlite__stmt_t *stmt;
1334 serialized = svn_skel__unparse(work_item, scratch_pool);
1335 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_WORK_ITEM));
1336 SVN_ERR(svn_sqlite__bind_blob(stmt, 1, serialized->data, serialized->len));
1337 return svn_error_trace(svn_sqlite__insert(NULL, stmt));
1341 /* Add work item(s) to the given SDB. Also see add_single_work_item(). This
1342 SKEL is usually passed to the various wc_db operation functions. It may
1343 be NULL, indicating no additional work items are needed, it may be a
1344 single work item, or it may be a list of work items. */
1345 static svn_error_t *
1346 add_work_items(svn_sqlite__db_t *sdb,
1347 const svn_skel_t *skel,
1348 apr_pool_t *scratch_pool)
1350 apr_pool_t *iterpool;
1352 /* Maybe there are no work items to insert. */
1354 return SVN_NO_ERROR;
1356 /* Should have a list. */
1357 SVN_ERR_ASSERT(!skel->is_atom);
1359 /* Is the list a single work item? Or a list of work items? */
1360 if (SVN_WC__SINGLE_WORK_ITEM(skel))
1361 return svn_error_trace(add_single_work_item(sdb, skel, scratch_pool));
1363 /* SKEL is a list-of-lists, aka list of work items. */
1365 iterpool = svn_pool_create(scratch_pool);
1366 for (skel = skel->children; skel; skel = skel->next)
1368 svn_pool_clear(iterpool);
1370 SVN_ERR(add_single_work_item(sdb, skel, iterpool));
1372 svn_pool_destroy(iterpool);
1374 return SVN_NO_ERROR;
1378 /* Determine whether the node exists for a given WCROOT and LOCAL_RELPATH. */
1379 static svn_error_t *
1380 does_node_exist(svn_boolean_t *exists,
1381 const svn_wc__db_wcroot_t *wcroot,
1382 const char *local_relpath)
1384 svn_sqlite__stmt_t *stmt;
1386 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DOES_NODE_EXIST));
1387 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1388 SVN_ERR(svn_sqlite__step(exists, stmt));
1390 return svn_error_trace(svn_sqlite__reset(stmt));
1394 svn_wc__db_install_schema_statistics(svn_sqlite__db_t *sdb,
1395 apr_pool_t *scratch_pool)
1397 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_INSTALL_SCHEMA_STATISTICS));
1399 return SVN_NO_ERROR;
1402 /* Helper for create_db(). Initializes our wc.db schema.
1404 static svn_error_t *
1405 init_db(/* output values */
1406 apr_int64_t *repos_id,
1409 svn_sqlite__db_t *db,
1410 const char *repos_root_url,
1411 const char *repos_uuid,
1412 const char *root_node_repos_relpath,
1413 svn_revnum_t root_node_revision,
1414 svn_depth_t root_node_depth,
1415 apr_pool_t *scratch_pool)
1417 svn_sqlite__stmt_t *stmt;
1419 /* Create the database's schema. */
1420 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_SCHEMA));
1421 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES));
1422 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES_TRIGGERS));
1423 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_EXTERNALS));
1425 /* Insert the repository. */
1426 SVN_ERR(create_repos_id(repos_id, repos_root_url, repos_uuid,
1429 SVN_ERR(svn_wc__db_install_schema_statistics(db, scratch_pool));
1431 /* Insert the wcroot. */
1432 /* ### Right now, this just assumes wc metadata is being stored locally. */
1433 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT));
1434 SVN_ERR(svn_sqlite__insert(wc_id, stmt));
1436 if (root_node_repos_relpath)
1438 svn_wc__db_status_t status = svn_wc__db_status_normal;
1440 if (root_node_revision > 0)
1441 status = svn_wc__db_status_incomplete; /* Will be filled by update */
1443 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_NODE));
1444 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtst",
1447 0, /* op_depth is 0 for base */
1450 root_node_repos_relpath,
1452 presence_map, status, /* 8 */
1453 svn_token__to_word(depth_map,
1455 kind_map, svn_node_dir /* 10 */));
1457 SVN_ERR(svn_sqlite__insert(NULL, stmt));
1460 return SVN_NO_ERROR;
1463 /* Create an sqlite database at DIR_ABSPATH/SDB_FNAME and insert
1464 records for REPOS_ID (using REPOS_ROOT_URL and REPOS_UUID) into
1465 REPOSITORY and for WC_ID into WCROOT. Return the DB connection
1468 If ROOT_NODE_REPOS_RELPATH is not NULL, insert a BASE node at
1469 the working copy root with repository relpath ROOT_NODE_REPOS_RELPATH,
1470 revision ROOT_NODE_REVISION and depth ROOT_NODE_DEPTH.
1472 static svn_error_t *
1473 create_db(svn_sqlite__db_t **sdb,
1474 apr_int64_t *repos_id,
1476 const char *dir_abspath,
1477 const char *repos_root_url,
1478 const char *repos_uuid,
1479 const char *sdb_fname,
1480 const char *root_node_repos_relpath,
1481 svn_revnum_t root_node_revision,
1482 svn_depth_t root_node_depth,
1483 svn_boolean_t exclusive,
1484 apr_pool_t *result_pool,
1485 apr_pool_t *scratch_pool)
1487 SVN_ERR(svn_wc__db_util_open_db(sdb, dir_abspath, sdb_fname,
1488 svn_sqlite__mode_rwcreate, exclusive,
1489 NULL /* my_statements */,
1490 result_pool, scratch_pool));
1492 SVN_SQLITE__WITH_LOCK(init_db(repos_id, wc_id,
1493 *sdb, repos_root_url, repos_uuid,
1494 root_node_repos_relpath, root_node_revision,
1495 root_node_depth, scratch_pool),
1498 return SVN_NO_ERROR;
1503 svn_wc__db_init(svn_wc__db_t *db,
1504 const char *local_abspath,
1505 const char *repos_relpath,
1506 const char *repos_root_url,
1507 const char *repos_uuid,
1508 svn_revnum_t initial_rev,
1510 apr_pool_t *scratch_pool)
1512 svn_sqlite__db_t *sdb;
1513 apr_int64_t repos_id;
1515 svn_wc__db_wcroot_t *wcroot;
1516 svn_boolean_t sqlite_exclusive = FALSE;
1518 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1519 SVN_ERR_ASSERT(repos_relpath != NULL);
1520 SVN_ERR_ASSERT(depth == svn_depth_empty
1521 || depth == svn_depth_files
1522 || depth == svn_depth_immediates
1523 || depth == svn_depth_infinity);
1525 /* ### REPOS_ROOT_URL and REPOS_UUID may be NULL. ... more doc: tbd */
1527 SVN_ERR(svn_config_get_bool((svn_config_t *)db->config, &sqlite_exclusive,
1528 SVN_CONFIG_SECTION_WORKING_COPY,
1529 SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
1532 /* Create the SDB and insert the basic rows. */
1533 SVN_ERR(create_db(&sdb, &repos_id, &wc_id, local_abspath, repos_root_url,
1534 repos_uuid, SDB_FILE,
1535 repos_relpath, initial_rev, depth, sqlite_exclusive,
1536 db->state_pool, scratch_pool));
1538 /* Create the WCROOT for this directory. */
1539 SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
1540 apr_pstrdup(db->state_pool, local_abspath),
1541 sdb, wc_id, FORMAT_FROM_SDB,
1542 FALSE /* auto-upgrade */,
1543 FALSE /* enforce_empty_wq */,
1544 db->state_pool, scratch_pool));
1546 /* The WCROOT is complete. Stash it into DB. */
1547 svn_hash_sets(db->dir_data, wcroot->abspath, wcroot);
1549 return SVN_NO_ERROR;
1554 svn_wc__db_to_relpath(const char **local_relpath,
1556 const char *wri_abspath,
1557 const char *local_abspath,
1558 apr_pool_t *result_pool,
1559 apr_pool_t *scratch_pool)
1561 svn_wc__db_wcroot_t *wcroot;
1562 const char *relpath;
1564 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1566 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &relpath, db,
1567 wri_abspath, result_pool, scratch_pool));
1569 /* This function is indirectly called from the upgrade code, so we
1570 can't verify the wcroot here. Just check that it is not NULL */
1571 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1573 if (svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
1575 *local_relpath = apr_pstrdup(result_pool,
1576 svn_dirent_skip_ancestor(wcroot->abspath,
1580 /* Probably moving from $TMP. Should we allow this? */
1581 *local_relpath = apr_pstrdup(result_pool, local_abspath);
1583 return SVN_NO_ERROR;
1588 svn_wc__db_from_relpath(const char **local_abspath,
1590 const char *wri_abspath,
1591 const char *local_relpath,
1592 apr_pool_t *result_pool,
1593 apr_pool_t *scratch_pool)
1595 svn_wc__db_wcroot_t *wcroot;
1596 const char *unused_relpath;
1598 SVN_ERR_ASSERT(svn_relpath_is_canonical(local_relpath));
1601 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1602 wri_abspath, scratch_pool, scratch_pool));
1604 /* This function is indirectly called from the upgrade code, so we
1605 can't verify the wcroot here. Just check that it is not NULL */
1606 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1609 *local_abspath = svn_dirent_join(wcroot->abspath,
1612 return SVN_NO_ERROR;
1617 svn_wc__db_get_wcroot(const char **wcroot_abspath,
1619 const char *wri_abspath,
1620 apr_pool_t *result_pool,
1621 apr_pool_t *scratch_pool)
1623 svn_wc__db_wcroot_t *wcroot;
1624 const char *unused_relpath;
1626 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1627 wri_abspath, scratch_pool, scratch_pool));
1629 /* Can't use VERIFY_USABLE_WCROOT, as this should be usable to detect
1630 where call upgrade */
1631 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1633 *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
1635 return SVN_NO_ERROR;
1640 svn_wc__db_base_add_directory(svn_wc__db_t *db,
1641 const char *local_abspath,
1642 const char *wri_abspath,
1643 const char *repos_relpath,
1644 const char *repos_root_url,
1645 const char *repos_uuid,
1646 svn_revnum_t revision,
1647 const apr_hash_t *props,
1648 svn_revnum_t changed_rev,
1649 apr_time_t changed_date,
1650 const char *changed_author,
1651 const apr_array_header_t *children,
1653 apr_hash_t *dav_cache,
1654 const svn_skel_t *conflict,
1655 svn_boolean_t update_actual_props,
1656 apr_hash_t *new_actual_props,
1657 apr_array_header_t *new_iprops,
1658 const svn_skel_t *work_items,
1659 apr_pool_t *scratch_pool)
1661 svn_wc__db_wcroot_t *wcroot;
1662 const char *local_relpath;
1663 insert_base_baton_t ibb;
1665 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1666 SVN_ERR_ASSERT(repos_relpath != NULL);
1667 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1668 SVN_ERR_ASSERT(repos_uuid != NULL);
1669 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1670 SVN_ERR_ASSERT(props != NULL);
1671 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1673 SVN_ERR_ASSERT(children != NULL);
1676 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1677 wri_abspath, scratch_pool, scratch_pool));
1678 VERIFY_USABLE_WCROOT(wcroot);
1679 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1683 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1684 ibb.repos_root_url = repos_root_url;
1685 ibb.repos_uuid = repos_uuid;
1687 ibb.status = svn_wc__db_status_normal;
1688 ibb.kind = svn_node_dir;
1689 ibb.repos_relpath = repos_relpath;
1690 ibb.revision = revision;
1692 ibb.iprops = new_iprops;
1694 ibb.changed_rev = changed_rev;
1695 ibb.changed_date = changed_date;
1696 ibb.changed_author = changed_author;
1698 ibb.children = children;
1701 ibb.dav_cache = dav_cache;
1702 ibb.conflict = conflict;
1703 ibb.work_items = work_items;
1705 if (update_actual_props)
1707 ibb.update_actual_props = TRUE;
1708 ibb.new_actual_props = new_actual_props;
1711 /* Insert the directory and all its children transactionally.
1713 Note: old children can stick around, even if they are no longer present
1714 in this directory's revision. */
1715 SVN_WC__DB_WITH_TXN(
1716 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1719 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
1720 return SVN_NO_ERROR;
1724 svn_wc__db_base_add_incomplete_directory(svn_wc__db_t *db,
1725 const char *local_abspath,
1726 const char *repos_relpath,
1727 const char *repos_root_url,
1728 const char *repos_uuid,
1729 svn_revnum_t revision,
1731 svn_boolean_t insert_base_deleted,
1732 svn_boolean_t delete_working,
1733 svn_skel_t *conflict,
1734 svn_skel_t *work_items,
1735 apr_pool_t *scratch_pool)
1737 svn_wc__db_wcroot_t *wcroot;
1738 const char *local_relpath;
1739 struct insert_base_baton_t ibb;
1741 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1742 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1743 SVN_ERR_ASSERT(repos_relpath && repos_root_url && repos_uuid);
1745 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
1747 scratch_pool, scratch_pool));
1749 VERIFY_USABLE_WCROOT(wcroot);
1753 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1754 ibb.repos_root_url = repos_root_url;
1755 ibb.repos_uuid = repos_uuid;
1757 ibb.status = svn_wc__db_status_incomplete;
1758 ibb.kind = svn_node_dir;
1759 ibb.repos_relpath = repos_relpath;
1760 ibb.revision = revision;
1762 ibb.insert_base_deleted = insert_base_deleted;
1763 ibb.delete_working = delete_working;
1765 ibb.conflict = conflict;
1766 ibb.work_items = work_items;
1768 SVN_WC__DB_WITH_TXN(
1769 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1772 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
1774 return SVN_NO_ERROR;
1779 svn_wc__db_base_add_file(svn_wc__db_t *db,
1780 const char *local_abspath,
1781 const char *wri_abspath,
1782 const char *repos_relpath,
1783 const char *repos_root_url,
1784 const char *repos_uuid,
1785 svn_revnum_t revision,
1786 const apr_hash_t *props,
1787 svn_revnum_t changed_rev,
1788 apr_time_t changed_date,
1789 const char *changed_author,
1790 const svn_checksum_t *checksum,
1791 apr_hash_t *dav_cache,
1792 svn_boolean_t delete_working,
1793 svn_boolean_t update_actual_props,
1794 apr_hash_t *new_actual_props,
1795 apr_array_header_t *new_iprops,
1796 svn_boolean_t keep_recorded_info,
1797 svn_boolean_t insert_base_deleted,
1798 const svn_skel_t *conflict,
1799 const svn_skel_t *work_items,
1800 apr_pool_t *scratch_pool)
1802 svn_wc__db_wcroot_t *wcroot;
1803 const char *local_relpath;
1804 insert_base_baton_t ibb;
1806 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1807 SVN_ERR_ASSERT(repos_relpath != NULL);
1808 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1809 SVN_ERR_ASSERT(repos_uuid != NULL);
1810 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1811 SVN_ERR_ASSERT(props != NULL);
1812 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1813 SVN_ERR_ASSERT(checksum != NULL);
1815 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1816 wri_abspath, scratch_pool, scratch_pool));
1817 VERIFY_USABLE_WCROOT(wcroot);
1818 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1822 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1823 ibb.repos_root_url = repos_root_url;
1824 ibb.repos_uuid = repos_uuid;
1826 ibb.status = svn_wc__db_status_normal;
1827 ibb.kind = svn_node_file;
1828 ibb.repos_relpath = repos_relpath;
1829 ibb.revision = revision;
1832 ibb.changed_rev = changed_rev;
1833 ibb.changed_date = changed_date;
1834 ibb.changed_author = changed_author;
1836 ibb.checksum = checksum;
1838 ibb.dav_cache = dav_cache;
1839 ibb.iprops = new_iprops;
1841 if (update_actual_props)
1843 ibb.update_actual_props = TRUE;
1844 ibb.new_actual_props = new_actual_props;
1847 ibb.keep_recorded_info = keep_recorded_info;
1848 ibb.insert_base_deleted = insert_base_deleted;
1849 ibb.delete_working = delete_working;
1851 ibb.conflict = conflict;
1852 ibb.work_items = work_items;
1854 SVN_WC__DB_WITH_TXN(
1855 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1858 /* If this used to be a directory we should remove children so pass
1859 * depth infinity. */
1860 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1862 return SVN_NO_ERROR;
1867 svn_wc__db_base_add_symlink(svn_wc__db_t *db,
1868 const char *local_abspath,
1869 const char *wri_abspath,
1870 const char *repos_relpath,
1871 const char *repos_root_url,
1872 const char *repos_uuid,
1873 svn_revnum_t revision,
1874 const apr_hash_t *props,
1875 svn_revnum_t changed_rev,
1876 apr_time_t changed_date,
1877 const char *changed_author,
1879 apr_hash_t *dav_cache,
1880 svn_boolean_t delete_working,
1881 svn_boolean_t update_actual_props,
1882 apr_hash_t *new_actual_props,
1883 apr_array_header_t *new_iprops,
1884 svn_boolean_t keep_recorded_info,
1885 svn_boolean_t insert_base_deleted,
1886 const svn_skel_t *conflict,
1887 const svn_skel_t *work_items,
1888 apr_pool_t *scratch_pool)
1890 svn_wc__db_wcroot_t *wcroot;
1891 const char *local_relpath;
1892 insert_base_baton_t ibb;
1894 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1895 SVN_ERR_ASSERT(repos_relpath != NULL);
1896 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1897 SVN_ERR_ASSERT(repos_uuid != NULL);
1898 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1899 SVN_ERR_ASSERT(props != NULL);
1900 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1901 SVN_ERR_ASSERT(target != NULL);
1903 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1904 wri_abspath, scratch_pool, scratch_pool));
1905 VERIFY_USABLE_WCROOT(wcroot);
1906 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1909 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1910 ibb.repos_root_url = repos_root_url;
1911 ibb.repos_uuid = repos_uuid;
1913 ibb.status = svn_wc__db_status_normal;
1914 ibb.kind = svn_node_symlink;
1915 ibb.repos_relpath = repos_relpath;
1916 ibb.revision = revision;
1919 ibb.changed_rev = changed_rev;
1920 ibb.changed_date = changed_date;
1921 ibb.changed_author = changed_author;
1923 ibb.target = target;
1925 ibb.dav_cache = dav_cache;
1926 ibb.iprops = new_iprops;
1928 if (update_actual_props)
1930 ibb.update_actual_props = TRUE;
1931 ibb.new_actual_props = new_actual_props;
1934 ibb.keep_recorded_info = keep_recorded_info;
1935 ibb.insert_base_deleted = insert_base_deleted;
1936 ibb.delete_working = delete_working;
1938 ibb.conflict = conflict;
1939 ibb.work_items = work_items;
1941 SVN_WC__DB_WITH_TXN(
1942 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1945 /* If this used to be a directory we should remove children so pass
1946 * depth infinity. */
1947 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1949 return SVN_NO_ERROR;
1953 static svn_error_t *
1954 add_excluded_or_not_present_node(svn_wc__db_t *db,
1955 const char *local_abspath,
1956 const char *repos_relpath,
1957 const char *repos_root_url,
1958 const char *repos_uuid,
1959 svn_revnum_t revision,
1960 svn_node_kind_t kind,
1961 svn_wc__db_status_t status,
1962 const svn_skel_t *conflict,
1963 const svn_skel_t *work_items,
1964 apr_pool_t *scratch_pool)
1966 svn_wc__db_wcroot_t *wcroot;
1967 const char *local_relpath;
1968 insert_base_baton_t ibb;
1969 const char *dir_abspath, *name;
1971 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1972 SVN_ERR_ASSERT(repos_relpath != NULL);
1973 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1974 SVN_ERR_ASSERT(repos_uuid != NULL);
1975 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1976 SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
1977 || status == svn_wc__db_status_excluded
1978 || status == svn_wc__db_status_not_present);
1980 /* These absent presence nodes are only useful below a parent node that is
1981 present. To avoid problems with working copies obstructing the child
1982 we calculate the wcroot and local_relpath of the parent and then add
1985 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
1987 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1988 dir_abspath, scratch_pool, scratch_pool));
1989 VERIFY_USABLE_WCROOT(wcroot);
1991 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
1995 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1996 ibb.repos_root_url = repos_root_url;
1997 ibb.repos_uuid = repos_uuid;
1999 ibb.status = status;
2001 ibb.repos_relpath = repos_relpath;
2002 ibb.revision = revision;
2004 /* Depending upon KIND, any of these might get used. */
2005 ibb.children = NULL;
2006 ibb.depth = svn_depth_unknown;
2007 ibb.checksum = NULL;
2010 ibb.conflict = conflict;
2011 ibb.work_items = work_items;
2013 SVN_WC__DB_WITH_TXN(
2014 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
2017 /* If this used to be a directory we should remove children so pass
2018 * depth infinity. */
2019 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2022 return SVN_NO_ERROR;
2027 svn_wc__db_base_add_excluded_node(svn_wc__db_t *db,
2028 const char *local_abspath,
2029 const char *repos_relpath,
2030 const char *repos_root_url,
2031 const char *repos_uuid,
2032 svn_revnum_t revision,
2033 svn_node_kind_t kind,
2034 svn_wc__db_status_t status,
2035 const svn_skel_t *conflict,
2036 const svn_skel_t *work_items,
2037 apr_pool_t *scratch_pool)
2039 SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
2040 || status == svn_wc__db_status_excluded);
2042 return add_excluded_or_not_present_node(
2043 db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2044 kind, status, conflict, work_items, scratch_pool);
2049 svn_wc__db_base_add_not_present_node(svn_wc__db_t *db,
2050 const char *local_abspath,
2051 const char *repos_relpath,
2052 const char *repos_root_url,
2053 const char *repos_uuid,
2054 svn_revnum_t revision,
2055 svn_node_kind_t kind,
2056 const svn_skel_t *conflict,
2057 const svn_skel_t *work_items,
2058 apr_pool_t *scratch_pool)
2060 return add_excluded_or_not_present_node(
2061 db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2062 kind, svn_wc__db_status_not_present, conflict, work_items, scratch_pool);
2065 /* Recursively clear moved-here information at the copy-half of the move
2066 * which moved the node at SRC_RELPATH away. This transforms the move into
2068 static svn_error_t *
2069 clear_moved_here(const char *src_relpath,
2070 svn_wc__db_wcroot_t *wcroot,
2071 apr_pool_t *scratch_pool)
2073 svn_sqlite__stmt_t *stmt;
2074 const char *dst_relpath;
2076 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
2077 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2078 src_relpath, relpath_depth(src_relpath)));
2079 SVN_ERR(svn_sqlite__step_row(stmt));
2080 dst_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
2081 SVN_ERR(svn_sqlite__reset(stmt));
2083 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2084 STMT_CLEAR_MOVED_HERE_RECURSIVE));
2085 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2086 dst_relpath, relpath_depth(dst_relpath)));
2087 SVN_ERR(svn_sqlite__step_done(stmt));
2089 return SVN_NO_ERROR;
2092 /* The body of svn_wc__db_base_remove().
2094 static svn_error_t *
2095 db_base_remove(svn_wc__db_wcroot_t *wcroot,
2096 const char *local_relpath,
2097 svn_wc__db_t *db, /* For checking conflicts */
2098 svn_boolean_t keep_as_working,
2099 svn_boolean_t queue_deletes,
2100 svn_boolean_t remove_locks,
2101 svn_revnum_t not_present_revision,
2102 svn_skel_t *conflict,
2103 svn_skel_t *work_items,
2104 apr_pool_t *scratch_pool)
2106 svn_sqlite__stmt_t *stmt;
2107 svn_boolean_t have_row;
2108 svn_wc__db_status_t status;
2109 apr_int64_t repos_id;
2110 const char *repos_relpath;
2111 svn_node_kind_t kind;
2112 svn_boolean_t keep_working;
2114 SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, NULL,
2115 &repos_relpath, &repos_id,
2116 NULL, NULL, NULL, NULL, NULL,
2117 NULL, NULL, NULL, NULL, NULL,
2118 wcroot, local_relpath,
2119 scratch_pool, scratch_pool));
2123 svn_sqlite__stmt_t *lock_stmt;
2125 SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
2126 STMT_DELETE_LOCK_RECURSIVELY));
2127 SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
2128 SVN_ERR(svn_sqlite__step_done(lock_stmt));
2131 if (status == svn_wc__db_status_normal
2134 SVN_ERR(svn_wc__db_op_make_copy(db,
2135 svn_dirent_join(wcroot->abspath,
2140 keep_working = TRUE;
2144 /* Check if there is already a working node */
2145 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2146 STMT_SELECT_WORKING_NODE));
2147 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2148 SVN_ERR(svn_sqlite__step(&keep_working, stmt));
2149 SVN_ERR(svn_sqlite__reset(stmt));
2152 /* Step 1: Create workqueue operations to remove files and dirs in the
2156 && (status == svn_wc__db_status_normal
2157 || status == svn_wc__db_status_incomplete))
2159 svn_skel_t *work_item;
2160 const char *local_abspath;
2162 local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
2164 if (kind == svn_node_dir)
2166 apr_pool_t *iterpool;
2167 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2168 STMT_SELECT_BASE_PRESENT));
2169 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2171 iterpool = svn_pool_create(scratch_pool);
2173 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2177 const char *node_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2178 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 1,
2180 const char *node_abspath;
2183 svn_pool_clear(iterpool);
2185 node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
2188 if (node_kind == svn_node_dir)
2189 err = svn_wc__wq_build_dir_remove(&work_item,
2190 db, wcroot->abspath,
2191 node_abspath, FALSE,
2192 iterpool, iterpool);
2194 err = svn_wc__wq_build_file_remove(&work_item,
2198 iterpool, iterpool);
2201 err = add_work_items(wcroot->sdb, work_item, iterpool);
2203 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2205 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2208 SVN_ERR(svn_sqlite__reset(stmt));
2210 SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
2211 db, wcroot->abspath,
2212 local_abspath, FALSE,
2213 scratch_pool, iterpool));
2214 svn_pool_destroy(iterpool);
2217 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
2218 db, wcroot->abspath,
2220 scratch_pool, scratch_pool));
2222 SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
2225 /* Step 2: Delete ACTUAL nodes */
2228 /* There won't be a record in NODE left for this node, so we want
2229 to remove *all* ACTUAL nodes, including ACTUAL ONLY. */
2230 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2231 STMT_DELETE_ACTUAL_NODE_RECURSIVE));
2232 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2233 SVN_ERR(svn_sqlite__step_done(stmt));
2235 else if (! keep_as_working)
2237 /* Delete only the ACTUAL nodes that apply to a delete of a BASE node */
2238 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2239 STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE));
2240 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2241 SVN_ERR(svn_sqlite__step_done(stmt));
2243 /* Else: Everything has been turned into a copy, so we want to keep all
2244 ACTUAL_NODE records */
2246 /* Step 3: Delete WORKING nodes */
2249 apr_pool_t *iterpool;
2252 * When deleting a conflicted node, moves of any moved-outside children
2253 * of the node must be broken. Else, the destination will still be marked
2254 * moved-here after the move source disappears from the working copy.
2256 * ### FIXME: It would be nicer to have the conflict resolver
2257 * break the move instead. It might also be a good idea to
2258 * flag a tree conflict on each moved-away child. But doing so
2259 * might introduce actual-only nodes without direct parents,
2260 * and we're not yet sure if other existing code is prepared
2261 * to handle such nodes. To be revisited post-1.8.
2263 * ### In case of a conflict we are most likely creating WORKING nodes
2264 * describing a copy of what was in BASE. The move information
2265 * should be updated to describe a move from the WORKING layer.
2266 * When stored that way the resolver of the tree conflict still has
2267 * the knowledge of what was moved.
2269 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2270 STMT_SELECT_MOVED_OUTSIDE));
2271 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2273 relpath_depth(local_relpath)));
2274 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2275 iterpool = svn_pool_create(scratch_pool);
2278 const char *child_relpath;
2281 svn_pool_clear(iterpool);
2282 child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
2283 err = clear_moved_here(child_relpath, wcroot, iterpool);
2285 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2286 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2288 svn_pool_destroy(iterpool);
2289 SVN_ERR(svn_sqlite__reset(stmt));
2293 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2294 STMT_DELETE_WORKING_BASE_DELETE));
2295 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2296 SVN_ERR(svn_sqlite__step_done(stmt));
2300 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2301 STMT_DELETE_WORKING_RECURSIVE));
2302 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2303 SVN_ERR(svn_sqlite__step_done(stmt));
2306 /* Step 4: Delete the BASE node descendants */
2307 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2308 STMT_DELETE_BASE_RECURSIVE));
2309 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2310 SVN_ERR(svn_sqlite__step_done(stmt));
2312 /* Step 5: handle the BASE node itself */
2313 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2314 STMT_DELETE_BASE_NODE));
2315 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2316 SVN_ERR(svn_sqlite__step_done(stmt));
2318 SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
2321 /* Step 6: Delete actual node if we don't keep working */
2324 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2325 STMT_DELETE_ACTUAL_NODE));
2326 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2327 SVN_ERR(svn_sqlite__step_done(stmt));
2330 if (SVN_IS_VALID_REVNUM(not_present_revision))
2332 struct insert_base_baton_t ibb;
2335 ibb.repos_id = repos_id;
2336 ibb.status = svn_wc__db_status_not_present;
2338 ibb.repos_relpath = repos_relpath;
2339 ibb.revision = not_present_revision;
2341 /* Depending upon KIND, any of these might get used. */
2342 ibb.children = NULL;
2343 ibb.depth = svn_depth_unknown;
2344 ibb.checksum = NULL;
2347 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
2350 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2352 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
2353 conflict, scratch_pool));
2355 return SVN_NO_ERROR;
2360 svn_wc__db_base_remove(svn_wc__db_t *db,
2361 const char *local_abspath,
2362 svn_boolean_t keep_as_working,
2363 svn_boolean_t queue_deletes,
2364 svn_boolean_t remove_locks,
2365 svn_revnum_t not_present_revision,
2366 svn_skel_t *conflict,
2367 svn_skel_t *work_items,
2368 apr_pool_t *scratch_pool)
2370 svn_wc__db_wcroot_t *wcroot;
2371 const char *local_relpath;
2373 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2375 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2376 local_abspath, scratch_pool, scratch_pool));
2377 VERIFY_USABLE_WCROOT(wcroot);
2379 SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath,
2380 db, keep_as_working, queue_deletes,
2381 remove_locks, not_present_revision,
2382 conflict, work_items, scratch_pool),
2385 /* If this used to be a directory we should remove children so pass
2386 * depth infinity. */
2387 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2390 return SVN_NO_ERROR;
2395 svn_wc__db_base_get_info_internal(svn_wc__db_status_t *status,
2396 svn_node_kind_t *kind,
2397 svn_revnum_t *revision,
2398 const char **repos_relpath,
2399 apr_int64_t *repos_id,
2400 svn_revnum_t *changed_rev,
2401 apr_time_t *changed_date,
2402 const char **changed_author,
2404 const svn_checksum_t **checksum,
2405 const char **target,
2406 svn_wc__db_lock_t **lock,
2407 svn_boolean_t *had_props,
2409 svn_boolean_t *update_root,
2410 svn_wc__db_wcroot_t *wcroot,
2411 const char *local_relpath,
2412 apr_pool_t *result_pool,
2413 apr_pool_t *scratch_pool)
2415 svn_sqlite__stmt_t *stmt;
2416 svn_boolean_t have_row;
2417 svn_error_t *err = SVN_NO_ERROR;
2419 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2420 lock ? STMT_SELECT_BASE_NODE_WITH_LOCK
2421 : STMT_SELECT_BASE_NODE));
2422 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2423 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2427 svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2429 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2437 *status = node_status;
2439 repos_location_from_columns(repos_id, revision, repos_relpath,
2440 stmt, 0, 4, 1, result_pool);
2441 SVN_ERR_ASSERT(!repos_id || *repos_id != INVALID_REPOS_ID);
2442 SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
2445 *lock = lock_from_columns(stmt, 15, 16, 17, 18, result_pool);
2449 *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2453 *changed_date = svn_sqlite__column_int64(stmt, 8);
2457 /* Result may be NULL. */
2458 *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2462 if (node_kind != svn_node_dir)
2464 *depth = svn_depth_unknown;
2468 *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2474 if (node_kind != svn_node_file)
2480 err = svn_sqlite__column_checksum(checksum, stmt, 5,
2483 err = svn_error_createf(
2485 _("The node '%s' has a corrupt checksum value."),
2486 path_for_error_message(wcroot, local_relpath,
2492 if (node_kind != svn_node_symlink)
2495 *target = svn_sqlite__column_text(stmt, 11, result_pool);
2499 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2503 if (node_status == svn_wc__db_status_normal
2504 || node_status == svn_wc__db_status_incomplete)
2506 SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2507 result_pool, scratch_pool));
2509 *props = apr_hash_make(result_pool);
2513 assert(svn_sqlite__column_is_null(stmt, 13));
2519 /* It's an update root iff it's a file external. */
2520 *update_root = svn_sqlite__column_boolean(stmt, 14);
2525 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2526 _("The node '%s' was not found."),
2527 path_for_error_message(wcroot, local_relpath,
2531 /* Note: given the composition, no need to wrap for tracing. */
2532 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2537 svn_wc__db_base_get_info(svn_wc__db_status_t *status,
2538 svn_node_kind_t *kind,
2539 svn_revnum_t *revision,
2540 const char **repos_relpath,
2541 const char **repos_root_url,
2542 const char **repos_uuid,
2543 svn_revnum_t *changed_rev,
2544 apr_time_t *changed_date,
2545 const char **changed_author,
2547 const svn_checksum_t **checksum,
2548 const char **target,
2549 svn_wc__db_lock_t **lock,
2550 svn_boolean_t *had_props,
2552 svn_boolean_t *update_root,
2554 const char *local_abspath,
2555 apr_pool_t *result_pool,
2556 apr_pool_t *scratch_pool)
2558 svn_wc__db_wcroot_t *wcroot;
2559 const char *local_relpath;
2560 apr_int64_t repos_id;
2562 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2564 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2565 local_abspath, scratch_pool, scratch_pool));
2566 VERIFY_USABLE_WCROOT(wcroot);
2568 SVN_ERR(svn_wc__db_base_get_info_internal(status, kind, revision,
2569 repos_relpath, &repos_id,
2570 changed_rev, changed_date,
2571 changed_author, depth,
2572 checksum, target, lock,
2573 had_props, props, update_root,
2574 wcroot, local_relpath,
2575 result_pool, scratch_pool));
2576 SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
2577 SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
2578 wcroot->sdb, repos_id, result_pool));
2580 return SVN_NO_ERROR;
2584 svn_wc__db_base_get_children_info(apr_hash_t **nodes,
2586 const char *dir_abspath,
2587 apr_pool_t *result_pool,
2588 apr_pool_t *scratch_pool)
2590 svn_wc__db_wcroot_t *wcroot;
2591 const char *local_relpath;
2592 svn_sqlite__stmt_t *stmt;
2593 svn_boolean_t have_row;
2595 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
2597 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2598 dir_abspath, scratch_pool, scratch_pool));
2599 VERIFY_USABLE_WCROOT(wcroot);
2601 *nodes = apr_hash_make(result_pool);
2603 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2604 STMT_SELECT_BASE_CHILDREN_INFO));
2605 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2607 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2611 struct svn_wc__db_base_info_t *info;
2613 apr_int64_t repos_id;
2614 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2615 const char *name = svn_relpath_basename(child_relpath, result_pool);
2617 info = apr_pcalloc(result_pool, sizeof(*info));
2619 repos_id = svn_sqlite__column_int64(stmt, 1);
2620 info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
2621 info->status = svn_sqlite__column_token(stmt, 3, presence_map);
2622 info->kind = svn_sqlite__column_token(stmt, 4, kind_map);
2623 info->revnum = svn_sqlite__column_revnum(stmt, 5);
2625 info->depth = svn_sqlite__column_token_null(stmt, 6, depth_map,
2628 info->update_root = svn_sqlite__column_boolean(stmt, 7);
2630 info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool);
2632 err = svn_wc__db_fetch_repos_info(&info->repos_root_url, NULL,
2633 wcroot->sdb, repos_id, result_pool);
2636 return svn_error_trace(
2637 svn_error_compose_create(err,
2638 svn_sqlite__reset(stmt)));
2641 svn_hash_sets(*nodes, name, info);
2643 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2646 SVN_ERR(svn_sqlite__reset(stmt));
2648 return SVN_NO_ERROR;
2653 svn_wc__db_base_get_props(apr_hash_t **props,
2655 const char *local_abspath,
2656 apr_pool_t *result_pool,
2657 apr_pool_t *scratch_pool)
2659 svn_wc__db_status_t presence;
2661 SVN_ERR(svn_wc__db_base_get_info(&presence, NULL, NULL, NULL, NULL,
2662 NULL, NULL, NULL, NULL, NULL,
2663 NULL, NULL, NULL, NULL, props, NULL,
2665 result_pool, scratch_pool));
2666 if (presence != svn_wc__db_status_normal
2667 && presence != svn_wc__db_status_incomplete)
2669 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2670 _("The node '%s' has a BASE status that"
2671 " has no properties."),
2672 svn_dirent_local_style(local_abspath,
2676 return SVN_NO_ERROR;
2681 svn_wc__db_base_get_children(const apr_array_header_t **children,
2683 const char *local_abspath,
2684 apr_pool_t *result_pool,
2685 apr_pool_t *scratch_pool)
2687 svn_wc__db_wcroot_t *wcroot;
2688 const char *local_relpath;
2690 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2692 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2694 scratch_pool, scratch_pool));
2695 VERIFY_USABLE_WCROOT(wcroot);
2697 return gather_repo_children(children, wcroot, local_relpath, 0,
2698 result_pool, scratch_pool);
2703 svn_wc__db_base_set_dav_cache(svn_wc__db_t *db,
2704 const char *local_abspath,
2705 const apr_hash_t *props,
2706 apr_pool_t *scratch_pool)
2708 svn_sqlite__stmt_t *stmt;
2711 SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
2712 STMT_UPDATE_BASE_NODE_DAV_CACHE,
2714 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
2716 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2718 if (affected_rows != 1)
2719 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2720 _("The node '%s' was not found."),
2721 svn_dirent_local_style(local_abspath,
2724 return SVN_NO_ERROR;
2729 svn_wc__db_base_get_dav_cache(apr_hash_t **props,
2731 const char *local_abspath,
2732 apr_pool_t *result_pool,
2733 apr_pool_t *scratch_pool)
2735 svn_sqlite__stmt_t *stmt;
2736 svn_boolean_t have_row;
2738 SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
2739 STMT_SELECT_BASE_DAV_CACHE, scratch_pool));
2740 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2742 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
2743 svn_sqlite__reset(stmt),
2744 _("The node '%s' was not found."),
2745 svn_dirent_local_style(local_abspath,
2748 SVN_ERR(svn_sqlite__column_properties(props, stmt, 0, result_pool,
2750 return svn_error_trace(svn_sqlite__reset(stmt));
2755 svn_wc__db_base_clear_dav_cache_recursive(svn_wc__db_t *db,
2756 const char *local_abspath,
2757 apr_pool_t *scratch_pool)
2759 svn_wc__db_wcroot_t *wcroot;
2760 const char *local_relpath;
2761 svn_sqlite__stmt_t *stmt;
2763 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2765 scratch_pool, scratch_pool));
2766 VERIFY_USABLE_WCROOT(wcroot);
2768 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2769 STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE));
2770 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2772 SVN_ERR(svn_sqlite__step_done(stmt));
2774 return SVN_NO_ERROR;
2779 svn_wc__db_depth_get_info(svn_wc__db_status_t *status,
2780 svn_node_kind_t *kind,
2781 svn_revnum_t *revision,
2782 const char **repos_relpath,
2783 apr_int64_t *repos_id,
2784 svn_revnum_t *changed_rev,
2785 apr_time_t *changed_date,
2786 const char **changed_author,
2788 const svn_checksum_t **checksum,
2789 const char **target,
2790 svn_boolean_t *had_props,
2792 svn_wc__db_wcroot_t *wcroot,
2793 const char *local_relpath,
2795 apr_pool_t *result_pool,
2796 apr_pool_t *scratch_pool)
2798 svn_sqlite__stmt_t *stmt;
2799 svn_boolean_t have_row;
2800 svn_error_t *err = SVN_NO_ERROR;
2802 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2803 STMT_SELECT_DEPTH_NODE));
2804 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
2805 wcroot->wc_id, local_relpath, op_depth));
2806 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2810 svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2812 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2820 *status = node_status;
2823 SVN_ERR(convert_to_working_status(status, *status));
2825 repos_location_from_columns(repos_id, revision, repos_relpath,
2826 stmt, 0, 4, 1, result_pool);
2830 *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2834 *changed_date = svn_sqlite__column_int64(stmt, 8);
2838 /* Result may be NULL. */
2839 *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2843 if (node_kind != svn_node_dir)
2845 *depth = svn_depth_unknown;
2849 *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2855 if (node_kind != svn_node_file)
2861 err = svn_sqlite__column_checksum(checksum, stmt, 5,
2864 err = svn_error_createf(
2866 _("The node '%s' has a corrupt checksum value."),
2867 path_for_error_message(wcroot, local_relpath,
2873 if (node_kind != svn_node_symlink)
2876 *target = svn_sqlite__column_text(stmt, 11, result_pool);
2880 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2884 if (node_status == svn_wc__db_status_normal
2885 || node_status == svn_wc__db_status_incomplete)
2887 SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2888 result_pool, scratch_pool));
2890 *props = apr_hash_make(result_pool);
2894 assert(svn_sqlite__column_is_null(stmt, 13));
2901 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2902 _("The node '%s' was not found."),
2903 path_for_error_message(wcroot, local_relpath,
2907 /* Note: given the composition, no need to wrap for tracing. */
2908 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2912 /* Baton for passing args to with_triggers(). */
2913 struct with_triggers_baton_t {
2916 svn_wc__db_txn_callback_t cb_func;
2920 /* Helper for creating SQLite triggers, running the main transaction
2921 callback, and then dropping the triggers. It guarantees that the
2922 triggers will not survive the transaction. This could be used for
2923 any general prefix/postscript statements where the postscript
2924 *must* be executed if the transaction completes.
2926 Implements svn_wc__db_txn_callback_t. */
2927 static svn_error_t *
2928 with_triggers(void *baton,
2929 svn_wc__db_wcroot_t *wcroot,
2930 const char *local_relpath,
2931 apr_pool_t *scratch_pool)
2933 struct with_triggers_baton_t *b = baton;
2937 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, b->create_trigger));
2939 err1 = b->cb_func(b->cb_baton, wcroot, local_relpath, scratch_pool);
2941 err2 = svn_sqlite__exec_statements(wcroot->sdb, b->drop_trigger);
2943 return svn_error_trace(svn_error_compose_create(err1, err2));
2947 /* Prototype for the "work callback" used by with_finalization(). */
2948 typedef svn_error_t * (*work_callback_t)(
2950 svn_wc__db_wcroot_t *wcroot,
2951 svn_cancel_func_t cancel_func,
2953 svn_wc_notify_func2_t notify_func,
2955 apr_pool_t *scratch_pool);
2957 /* Utility function to provide several features, with a guaranteed
2958 finalization (ie. to drop temporary tables).
2960 1) for WCROOT and LOCAL_RELPATH, run TXN_CB(TXN_BATON) within a
2962 2) if (1) is successful and a NOTIFY_FUNC is provided, then run
2963 the "work" step: WORK_CB(WORK_BATON).
2964 3) execute FINALIZE_STMT_IDX no matter what errors may be thrown
2965 from the above two steps.
2967 CANCEL_FUNC, CANCEL_BATON, NOTIFY_FUNC and NOTIFY_BATON are their
2968 typical values. These are passed to the work callback, which typically
2969 provides notification about the work done by TXN_CB. */
2970 static svn_error_t *
2971 with_finalization(svn_wc__db_wcroot_t *wcroot,
2972 const char *local_relpath,
2973 svn_wc__db_txn_callback_t txn_cb,
2975 work_callback_t work_cb,
2977 svn_cancel_func_t cancel_func,
2979 svn_wc_notify_func2_t notify_func,
2981 int finalize_stmt_idx,
2982 apr_pool_t *scratch_pool)
2987 err1 = svn_wc__db_with_txn(wcroot, local_relpath, txn_cb, txn_baton,
2990 if (err1 == NULL && notify_func != NULL)
2992 err2 = work_cb(work_baton, wcroot,
2993 cancel_func, cancel_baton,
2994 notify_func, notify_baton,
2996 err1 = svn_error_compose_create(err1, err2);
2999 err2 = svn_sqlite__exec_statements(wcroot->sdb, finalize_stmt_idx);
3001 return svn_error_trace(svn_error_compose_create(err1, err2));
3005 /* Initialize the baton with appropriate "blank" values. This allows the
3006 insertion function to leave certain columns null. */
3008 blank_ieb(insert_external_baton_t *ieb)
3010 memset(ieb, 0, sizeof(*ieb));
3011 ieb->revision = SVN_INVALID_REVNUM;
3012 ieb->changed_rev = SVN_INVALID_REVNUM;
3013 ieb->repos_id = INVALID_REPOS_ID;
3015 ieb->recorded_peg_revision = SVN_INVALID_REVNUM;
3016 ieb->recorded_revision = SVN_INVALID_REVNUM;
3019 /* Insert the externals row represented by (insert_external_baton_t *) BATON.
3021 * Implements svn_wc__db_txn_callback_t. */
3022 static svn_error_t *
3023 insert_external_node(const insert_external_baton_t *ieb,
3024 svn_wc__db_wcroot_t *wcroot,
3025 const char *local_relpath,
3026 apr_pool_t *scratch_pool)
3028 svn_wc__db_status_t status;
3030 svn_boolean_t update_root;
3031 apr_int64_t repos_id;
3032 svn_sqlite__stmt_t *stmt;
3034 if (ieb->repos_id != INVALID_REPOS_ID)
3035 repos_id = ieb->repos_id;
3037 SVN_ERR(create_repos_id(&repos_id, ieb->repos_root_url, ieb->repos_uuid,
3038 wcroot->sdb, scratch_pool));
3040 /* And there must be no existing BASE node or it must be a file external */
3041 err = svn_wc__db_base_get_info_internal(&status, NULL, NULL, NULL, NULL,
3042 NULL, NULL, NULL, NULL, NULL,
3043 NULL, NULL, NULL, NULL, &update_root,
3044 wcroot, local_relpath,
3045 scratch_pool, scratch_pool);
3048 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3049 return svn_error_trace(err);
3051 svn_error_clear(err);
3053 else if (status == svn_wc__db_status_normal && !update_root)
3054 return svn_error_create(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, NULL);
3056 if (ieb->kind == svn_node_file
3057 || ieb->kind == svn_node_symlink)
3059 struct insert_base_baton_t ibb;
3063 ibb.status = svn_wc__db_status_normal;
3064 ibb.kind = ieb->kind;
3066 ibb.repos_id = repos_id;
3067 ibb.repos_relpath = ieb->repos_relpath;
3068 ibb.revision = ieb->revision;
3070 ibb.props = ieb->props;
3071 ibb.iprops = ieb->iprops;
3072 ibb.changed_rev = ieb->changed_rev;
3073 ibb.changed_date = ieb->changed_date;
3074 ibb.changed_author = ieb->changed_author;
3076 ibb.dav_cache = ieb->dav_cache;
3078 ibb.checksum = ieb->checksum;
3079 ibb.target = ieb->target;
3081 ibb.conflict = ieb->conflict;
3083 ibb.update_actual_props = ieb->update_actual_props;
3084 ibb.new_actual_props = ieb->new_actual_props;
3086 ibb.keep_recorded_info = ieb->keep_recorded_info;
3088 ibb.work_items = ieb->work_items;
3090 ibb.file_external = TRUE;
3092 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
3095 SVN_ERR(add_work_items(wcroot->sdb, ieb->work_items, scratch_pool));
3097 /* The externals table only support presence normal and excluded */
3098 SVN_ERR_ASSERT(ieb->presence == svn_wc__db_status_normal
3099 || ieb->presence == svn_wc__db_status_excluded);
3101 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_EXTERNAL));
3103 SVN_ERR(svn_sqlite__bindf(stmt, "issttsis",
3106 svn_relpath_dirname(local_relpath,
3108 presence_map, ieb->presence,
3109 kind_map, ieb->kind,
3110 ieb->record_ancestor_relpath,
3112 ieb->recorded_repos_relpath));
3114 if (SVN_IS_VALID_REVNUM(ieb->recorded_peg_revision))
3115 SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, ieb->recorded_peg_revision));
3117 if (SVN_IS_VALID_REVNUM(ieb->recorded_revision))
3118 SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, ieb->recorded_revision));
3120 SVN_ERR(svn_sqlite__insert(NULL, stmt));
3122 return SVN_NO_ERROR;
3126 svn_wc__db_external_add_file(svn_wc__db_t *db,
3127 const char *local_abspath,
3128 const char *wri_abspath,
3130 const char *repos_relpath,
3131 const char *repos_root_url,
3132 const char *repos_uuid,
3133 svn_revnum_t revision,
3135 const apr_hash_t *props,
3136 apr_array_header_t *iprops,
3138 svn_revnum_t changed_rev,
3139 apr_time_t changed_date,
3140 const char *changed_author,
3142 const svn_checksum_t *checksum,
3144 const apr_hash_t *dav_cache,
3146 const char *record_ancestor_abspath,
3147 const char *recorded_repos_relpath,
3148 svn_revnum_t recorded_peg_revision,
3149 svn_revnum_t recorded_revision,
3151 svn_boolean_t update_actual_props,
3152 apr_hash_t *new_actual_props,
3154 svn_boolean_t keep_recorded_info,
3155 const svn_skel_t *conflict,
3156 const svn_skel_t *work_items,
3157 apr_pool_t *scratch_pool)
3159 svn_wc__db_wcroot_t *wcroot;
3160 const char *local_relpath;
3161 insert_external_baton_t ieb;
3163 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3166 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3168 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3169 wri_abspath, scratch_pool, scratch_pool));
3170 VERIFY_USABLE_WCROOT(wcroot);
3172 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3173 record_ancestor_abspath));
3175 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3177 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3181 ieb.kind = svn_node_file;
3182 ieb.presence = svn_wc__db_status_normal;
3184 ieb.repos_root_url = repos_root_url;
3185 ieb.repos_uuid = repos_uuid;
3187 ieb.repos_relpath = repos_relpath;
3188 ieb.revision = revision;
3191 ieb.iprops = iprops;
3193 ieb.changed_rev = changed_rev;
3194 ieb.changed_date = changed_date;
3195 ieb.changed_author = changed_author;
3197 ieb.checksum = checksum;
3199 ieb.dav_cache = dav_cache;
3201 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3203 record_ancestor_abspath);
3204 ieb.recorded_repos_relpath = recorded_repos_relpath;
3205 ieb.recorded_peg_revision = recorded_peg_revision;
3206 ieb.recorded_revision = recorded_revision;
3208 ieb.update_actual_props = update_actual_props;
3209 ieb.new_actual_props = new_actual_props;
3211 ieb.keep_recorded_info = keep_recorded_info;
3213 ieb.conflict = conflict;
3214 ieb.work_items = work_items;
3216 SVN_WC__DB_WITH_TXN(
3217 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3220 return SVN_NO_ERROR;
3224 svn_wc__db_external_add_symlink(svn_wc__db_t *db,
3225 const char *local_abspath,
3226 const char *wri_abspath,
3227 const char *repos_relpath,
3228 const char *repos_root_url,
3229 const char *repos_uuid,
3230 svn_revnum_t revision,
3231 const apr_hash_t *props,
3232 svn_revnum_t changed_rev,
3233 apr_time_t changed_date,
3234 const char *changed_author,
3236 const apr_hash_t *dav_cache,
3237 const char *record_ancestor_abspath,
3238 const char *recorded_repos_relpath,
3239 svn_revnum_t recorded_peg_revision,
3240 svn_revnum_t recorded_revision,
3241 svn_boolean_t update_actual_props,
3242 apr_hash_t *new_actual_props,
3243 svn_boolean_t keep_recorded_info,
3244 const svn_skel_t *work_items,
3245 apr_pool_t *scratch_pool)
3247 svn_wc__db_wcroot_t *wcroot;
3248 const char *local_relpath;
3249 insert_external_baton_t ieb;
3251 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3254 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3256 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3257 wri_abspath, scratch_pool, scratch_pool));
3258 VERIFY_USABLE_WCROOT(wcroot);
3260 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3261 record_ancestor_abspath));
3263 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3265 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3269 ieb.kind = svn_node_symlink;
3270 ieb.presence = svn_wc__db_status_normal;
3272 ieb.repos_root_url = repos_root_url;
3273 ieb.repos_uuid = repos_uuid;
3275 ieb.repos_relpath = repos_relpath;
3276 ieb.revision = revision;
3280 ieb.changed_rev = changed_rev;
3281 ieb.changed_date = changed_date;
3282 ieb.changed_author = changed_author;
3284 ieb.target = target;
3286 ieb.dav_cache = dav_cache;
3288 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3290 record_ancestor_abspath);
3291 ieb.recorded_repos_relpath = recorded_repos_relpath;
3292 ieb.recorded_peg_revision = recorded_peg_revision;
3293 ieb.recorded_revision = recorded_revision;
3295 ieb.update_actual_props = update_actual_props;
3296 ieb.new_actual_props = new_actual_props;
3298 ieb.keep_recorded_info = keep_recorded_info;
3300 ieb.work_items = work_items;
3302 SVN_WC__DB_WITH_TXN(
3303 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3306 return SVN_NO_ERROR;
3310 svn_wc__db_external_add_dir(svn_wc__db_t *db,
3311 const char *local_abspath,
3312 const char *wri_abspath,
3313 const char *repos_root_url,
3314 const char *repos_uuid,
3315 const char *record_ancestor_abspath,
3316 const char *recorded_repos_relpath,
3317 svn_revnum_t recorded_peg_revision,
3318 svn_revnum_t recorded_revision,
3319 const svn_skel_t *work_items,
3320 apr_pool_t *scratch_pool)
3322 svn_wc__db_wcroot_t *wcroot;
3323 const char *local_relpath;
3324 insert_external_baton_t ieb;
3326 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3329 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3331 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3332 wri_abspath, scratch_pool, scratch_pool));
3333 VERIFY_USABLE_WCROOT(wcroot);
3335 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3336 record_ancestor_abspath));
3338 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3340 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3344 ieb.kind = svn_node_dir;
3345 ieb.presence = svn_wc__db_status_normal;
3347 ieb.repos_root_url = repos_root_url;
3348 ieb.repos_uuid = repos_uuid;
3350 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3352 record_ancestor_abspath);
3353 ieb.recorded_repos_relpath = recorded_repos_relpath;
3354 ieb.recorded_peg_revision = recorded_peg_revision;
3355 ieb.recorded_revision = recorded_revision;
3357 ieb.work_items = work_items;
3359 SVN_WC__DB_WITH_TXN(
3360 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3363 return SVN_NO_ERROR;
3366 /* The body of svn_wc__db_external_remove(). */
3367 static svn_error_t *
3368 db_external_remove(const svn_skel_t *work_items,
3369 svn_wc__db_wcroot_t *wcroot,
3370 const char *local_relpath,
3371 apr_pool_t *scratch_pool)
3373 svn_sqlite__stmt_t *stmt;
3375 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3376 STMT_DELETE_EXTERNAL));
3377 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3378 SVN_ERR(svn_sqlite__step_done(stmt));
3380 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
3382 /* ### What about actual? */
3383 return SVN_NO_ERROR;
3387 svn_wc__db_external_remove(svn_wc__db_t *db,
3388 const char *local_abspath,
3389 const char *wri_abspath,
3390 const svn_skel_t *work_items,
3391 apr_pool_t *scratch_pool)
3393 svn_wc__db_wcroot_t *wcroot;
3394 const char *local_relpath;
3396 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3399 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3401 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3402 wri_abspath, scratch_pool, scratch_pool));
3403 VERIFY_USABLE_WCROOT(wcroot);
3405 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3407 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3409 SVN_WC__DB_WITH_TXN(db_external_remove(work_items, wcroot, local_relpath,
3413 return SVN_NO_ERROR;
3417 svn_wc__db_external_read(svn_wc__db_status_t *status,
3418 svn_node_kind_t *kind,
3419 const char **definining_abspath,
3420 const char **repos_root_url,
3421 const char **repos_uuid,
3422 const char **recorded_repos_relpath,
3423 svn_revnum_t *recorded_peg_revision,
3424 svn_revnum_t *recorded_revision,
3426 const char *local_abspath,
3427 const char *wri_abspath,
3428 apr_pool_t *result_pool,
3429 apr_pool_t *scratch_pool)
3431 svn_wc__db_wcroot_t *wcroot;
3432 const char *local_relpath;
3433 svn_sqlite__stmt_t *stmt;
3434 svn_boolean_t have_info;
3435 svn_error_t *err = NULL;
3436 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3439 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3441 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3442 wri_abspath, scratch_pool, scratch_pool));
3443 VERIFY_USABLE_WCROOT(wcroot);
3445 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3447 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3449 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3450 STMT_SELECT_EXTERNAL_INFO));
3451 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3452 SVN_ERR(svn_sqlite__step(&have_info, stmt));
3457 *status = svn_sqlite__column_token(stmt, 0, presence_map);
3460 *kind = svn_sqlite__column_token(stmt, 1, kind_map);
3462 if (definining_abspath)
3464 const char *record_relpath = svn_sqlite__column_text(stmt, 2, NULL);
3466 *definining_abspath = svn_dirent_join(wcroot->abspath,
3467 record_relpath, result_pool);
3470 if (repos_root_url || repos_uuid)
3472 apr_int64_t repos_id;
3474 repos_id = svn_sqlite__column_int64(stmt, 3);
3476 err = svn_error_compose_create(
3478 svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
3479 wcroot->sdb, repos_id,
3483 if (recorded_repos_relpath)
3484 *recorded_repos_relpath = svn_sqlite__column_text(stmt, 4,
3487 if (recorded_peg_revision)
3488 *recorded_peg_revision = svn_sqlite__column_revnum(stmt, 5);
3490 if (recorded_revision)
3491 *recorded_revision = svn_sqlite__column_revnum(stmt, 6);
3495 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3496 _("The node '%s' is not an external."),
3497 svn_dirent_local_style(local_abspath,
3501 return svn_error_trace(
3502 svn_error_compose_create(err, svn_sqlite__reset(stmt)));
3506 svn_wc__db_committable_externals_below(apr_array_header_t **externals,
3508 const char *local_abspath,
3509 svn_boolean_t immediates_only,
3510 apr_pool_t *result_pool,
3511 apr_pool_t *scratch_pool)
3513 svn_wc__db_wcroot_t *wcroot;
3514 svn_sqlite__stmt_t *stmt;
3515 const char *local_relpath;
3516 svn_boolean_t have_row;
3517 svn_wc__committable_external_info_t *info;
3518 svn_node_kind_t db_kind;
3519 apr_array_header_t *result = NULL;
3521 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3523 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3524 local_abspath, scratch_pool, scratch_pool));
3525 VERIFY_USABLE_WCROOT(wcroot);
3527 SVN_ERR(svn_sqlite__get_statement(
3530 ? STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW
3531 : STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW));
3533 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3535 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3538 result = apr_array_make(result_pool, 0,
3539 sizeof(svn_wc__committable_external_info_t *));
3543 info = apr_palloc(result_pool, sizeof(*info));
3545 local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3546 info->local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
3549 db_kind = svn_sqlite__column_token(stmt, 1, kind_map);
3550 SVN_ERR_ASSERT(db_kind == svn_node_file || db_kind == svn_node_dir);
3551 info->kind = db_kind;
3553 info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
3554 info->repos_root_url = svn_sqlite__column_text(stmt, 3, result_pool);
3556 APR_ARRAY_PUSH(result, svn_wc__committable_external_info_t *) = info;
3558 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3561 *externals = result;
3562 return svn_error_trace(svn_sqlite__reset(stmt));
3566 svn_wc__db_externals_defined_below(apr_hash_t **externals,
3568 const char *local_abspath,
3569 apr_pool_t *result_pool,
3570 apr_pool_t *scratch_pool)
3572 svn_wc__db_wcroot_t *wcroot;
3573 svn_sqlite__stmt_t *stmt;
3574 const char *local_relpath;
3575 svn_boolean_t have_row;
3577 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3579 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3580 local_abspath, scratch_pool, scratch_pool));
3581 VERIFY_USABLE_WCROOT(wcroot);
3583 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3584 STMT_SELECT_EXTERNALS_DEFINED));
3586 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3588 *externals = apr_hash_make(result_pool);
3589 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3593 const char *def_local_relpath;
3595 local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3596 def_local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3598 svn_hash_sets(*externals,
3599 svn_dirent_join(wcroot->abspath, local_relpath,
3601 svn_dirent_join(wcroot->abspath, def_local_relpath,
3604 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3607 return svn_error_trace(svn_sqlite__reset(stmt));
3611 svn_wc__db_externals_gather_definitions(apr_hash_t **externals,
3612 apr_hash_t **depths,
3614 const char *local_abspath,
3615 apr_pool_t *result_pool,
3616 apr_pool_t *scratch_pool)
3618 svn_wc__db_wcroot_t *wcroot;
3619 svn_sqlite__stmt_t *stmt;
3620 const char *local_relpath;
3621 svn_boolean_t have_row;
3622 svn_error_t *err = NULL;
3623 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3625 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3627 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3628 local_abspath, scratch_pool, iterpool));
3629 VERIFY_USABLE_WCROOT(wcroot);
3631 *externals = apr_hash_make(result_pool);
3633 *depths = apr_hash_make(result_pool);
3635 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3636 STMT_SELECT_EXTERNAL_PROPERTIES));
3638 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3640 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3644 apr_hash_t *node_props;
3645 const char *external_value;
3647 svn_pool_clear(iterpool);
3648 err = svn_sqlite__column_properties(&node_props, stmt, 0, iterpool,
3654 external_value = svn_prop_get_value(node_props, SVN_PROP_EXTERNALS);
3658 const char *node_abspath;
3659 const char *node_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3661 node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
3664 svn_hash_sets(*externals, node_abspath,
3665 apr_pstrdup(result_pool, external_value));
3670 = svn_sqlite__column_token_null(stmt, 2, depth_map,
3673 svn_hash_sets(*depths, node_abspath,
3674 /* Use static string */
3675 svn_token__to_word(depth_map, depth));
3679 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3682 svn_pool_destroy(iterpool);
3684 return svn_error_trace(svn_error_compose_create(err,
3685 svn_sqlite__reset(stmt)));
3688 /* Copy the ACTUAL data for SRC_RELPATH and tweak it to refer to DST_RELPATH.
3689 The new ACTUAL data won't have any conflicts. */
3690 static svn_error_t *
3691 copy_actual(svn_wc__db_wcroot_t *src_wcroot,
3692 const char *src_relpath,
3693 svn_wc__db_wcroot_t *dst_wcroot,
3694 const char *dst_relpath,
3695 apr_pool_t *scratch_pool)
3697 svn_sqlite__stmt_t *stmt;
3698 svn_boolean_t have_row;
3700 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
3701 STMT_SELECT_ACTUAL_NODE));
3702 SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
3703 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3706 apr_size_t props_size;
3707 const char *changelist;
3708 const char *properties;
3710 /* Skipping conflict data... */
3711 changelist = svn_sqlite__column_text(stmt, 0, scratch_pool);
3712 /* No need to parse the properties when simply copying. */
3713 properties = svn_sqlite__column_blob(stmt, 1, &props_size, scratch_pool);
3715 if (changelist || properties)
3717 SVN_ERR(svn_sqlite__reset(stmt));
3719 SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
3720 STMT_INSERT_ACTUAL_NODE));
3721 SVN_ERR(svn_sqlite__bindf(stmt, "issbs",
3722 dst_wcroot->wc_id, dst_relpath,
3723 svn_relpath_dirname(dst_relpath, scratch_pool),
3724 properties, props_size, changelist));
3725 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3728 SVN_ERR(svn_sqlite__reset(stmt));
3730 return SVN_NO_ERROR;
3733 /* Helper for svn_wc__db_op_copy to handle copying from one db to
3735 static svn_error_t *
3736 cross_db_copy(svn_wc__db_wcroot_t *src_wcroot,
3737 const char *src_relpath,
3738 svn_wc__db_wcroot_t *dst_wcroot,
3739 const char *dst_relpath,
3740 svn_wc__db_status_t dst_status,
3742 int dst_np_op_depth,
3743 svn_node_kind_t kind,
3744 const apr_array_header_t *children,
3745 apr_int64_t copyfrom_id,
3746 const char *copyfrom_relpath,
3747 svn_revnum_t copyfrom_rev,
3748 apr_pool_t *scratch_pool)
3750 insert_working_baton_t iwb;
3751 svn_revnum_t changed_rev;
3752 apr_time_t changed_date;
3753 const char *changed_author;
3754 const svn_checksum_t *checksum;
3758 SVN_ERR_ASSERT(kind == svn_node_file
3759 || kind == svn_node_dir
3762 SVN_ERR(read_info(NULL, NULL, NULL, NULL, NULL,
3763 &changed_rev, &changed_date, &changed_author, &depth,
3764 &checksum, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3765 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3766 src_wcroot, src_relpath, scratch_pool, scratch_pool));
3768 SVN_ERR(db_read_pristine_props(&props, src_wcroot, src_relpath, FALSE,
3769 scratch_pool, scratch_pool));
3772 iwb.presence = dst_status;
3776 iwb.changed_rev = changed_rev;
3777 iwb.changed_date = changed_date;
3778 iwb.changed_author = changed_author;
3779 iwb.original_repos_id = copyfrom_id;
3780 iwb.original_repos_relpath = copyfrom_relpath;
3781 iwb.original_revnum = copyfrom_rev;
3782 iwb.moved_here = FALSE;
3784 iwb.op_depth = dst_op_depth;
3786 iwb.checksum = checksum;
3787 iwb.children = children;
3790 iwb.not_present_op_depth = dst_np_op_depth;
3792 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath, scratch_pool));
3794 SVN_ERR(copy_actual(src_wcroot, src_relpath,
3795 dst_wcroot, dst_relpath, scratch_pool));
3797 return SVN_NO_ERROR;
3800 /* Helper for scan_deletion_txn. Extracts the moved-to information, if
3801 any, from STMT. Sets *SCAN to FALSE if moved-to was available. */
3802 static svn_error_t *
3803 get_moved_to(const char **moved_to_relpath_p,
3804 const char **moved_to_op_root_relpath_p,
3805 svn_boolean_t *scan,
3806 svn_sqlite__stmt_t *stmt,
3807 const char *current_relpath,
3808 svn_wc__db_wcroot_t *wcroot,
3809 const char *local_relpath,
3810 apr_pool_t *result_pool,
3811 apr_pool_t *scratch_pool)
3813 const char *moved_to_relpath = svn_sqlite__column_text(stmt, 3, NULL);
3815 if (moved_to_relpath)
3817 const char *moved_to_op_root_relpath = moved_to_relpath;
3819 if (strcmp(current_relpath, local_relpath))
3821 /* LOCAL_RELPATH is a child inside the move op-root. */
3822 const char *moved_child_relpath;
3824 /* The CURRENT_RELPATH is the op_root of the delete-half of
3825 * the move. LOCAL_RELPATH is a child that was moved along.
3826 * Compute the child's new location within the move target. */
3827 moved_child_relpath = svn_relpath_skip_ancestor(current_relpath,
3829 SVN_ERR_ASSERT(moved_child_relpath &&
3830 strlen(moved_child_relpath) > 0);
3831 moved_to_relpath = svn_relpath_join(moved_to_op_root_relpath,
3832 moved_child_relpath,
3836 if (moved_to_op_root_relpath && moved_to_op_root_relpath_p)
3837 *moved_to_op_root_relpath_p
3838 = apr_pstrdup(result_pool, moved_to_op_root_relpath);
3840 if (moved_to_relpath && moved_to_relpath_p)
3842 = apr_pstrdup(result_pool, moved_to_relpath);
3847 return SVN_NO_ERROR;
3851 /* The body of svn_wc__db_scan_deletion().
3853 static svn_error_t *
3854 scan_deletion_txn(const char **base_del_relpath,
3855 const char **moved_to_relpath,
3856 const char **work_del_relpath,
3857 const char **moved_to_op_root_relpath,
3858 svn_wc__db_wcroot_t *wcroot,
3859 const char *local_relpath,
3860 apr_pool_t *result_pool,
3861 apr_pool_t *scratch_pool)
3863 const char *current_relpath = local_relpath;
3864 svn_sqlite__stmt_t *stmt;
3865 svn_wc__db_status_t work_presence;
3866 svn_boolean_t have_row, scan, have_base;
3869 /* Initialize all the OUT parameters. */
3870 if (base_del_relpath != NULL)
3871 *base_del_relpath = NULL;
3872 if (moved_to_relpath != NULL)
3873 *moved_to_relpath = NULL;
3874 if (work_del_relpath != NULL)
3875 *work_del_relpath = NULL;
3876 if (moved_to_op_root_relpath != NULL)
3877 *moved_to_op_root_relpath = NULL;
3879 /* If looking for moved-to info then we need to scan every path
3880 until we find it. If not looking for moved-to we only need to
3881 check op-roots and parents of op-roots. */
3882 scan = (moved_to_op_root_relpath || moved_to_relpath);
3884 SVN_ERR(svn_sqlite__get_statement(
3886 scan ? STMT_SELECT_DELETION_INFO_SCAN
3887 : STMT_SELECT_DELETION_INFO));
3889 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
3890 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3892 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
3893 _("The node '%s' was not found."),
3894 path_for_error_message(wcroot, local_relpath,
3897 work_presence = svn_sqlite__column_token(stmt, 1, presence_map);
3898 have_base = !svn_sqlite__column_is_null(stmt, 0);
3899 if (work_presence != svn_wc__db_status_not_present
3900 && work_presence != svn_wc__db_status_base_deleted)
3901 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
3902 svn_sqlite__reset(stmt),
3903 _("Expected node '%s' to be deleted."),
3904 path_for_error_message(wcroot, local_relpath,
3907 op_depth = svn_sqlite__column_int(stmt, 2);
3909 /* Special case: LOCAL_RELPATH not-present within a WORKING tree, we
3910 treat this as an op-root. At commit time we need to explicitly
3911 delete such nodes otherwise they will be present in the
3913 if (work_presence == svn_wc__db_status_not_present
3914 && work_del_relpath && !*work_del_relpath)
3916 *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
3918 if (!scan && !base_del_relpath)
3920 /* We have all we need, exit early */
3921 SVN_ERR(svn_sqlite__reset(stmt));
3922 return SVN_NO_ERROR;
3930 const char *parent_relpath;
3931 int current_depth = relpath_depth(current_relpath);
3933 /* Step CURRENT_RELPATH to op-root */
3939 err = get_moved_to(moved_to_relpath, moved_to_op_root_relpath,
3940 &scan, stmt, current_relpath,
3941 wcroot, local_relpath,
3942 result_pool, scratch_pool);
3944 && !base_del_relpath
3945 && !work_del_relpath))
3947 /* We have all we need (or an error occurred) */
3948 SVN_ERR(svn_sqlite__reset(stmt));
3949 return svn_error_trace(err);
3953 if (current_depth <= op_depth)
3956 current_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
3959 if (scan || current_depth == op_depth)
3961 SVN_ERR(svn_sqlite__reset(stmt));
3962 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
3964 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3965 SVN_ERR_ASSERT(have_row);
3966 have_base = !svn_sqlite__column_is_null(stmt, 0);
3969 SVN_ERR(svn_sqlite__reset(stmt));
3971 /* Now CURRENT_RELPATH is an op-root, have a look at the parent. */
3973 SVN_ERR_ASSERT(current_relpath[0] != '\0'); /* Catch invalid data */
3974 parent_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
3975 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
3976 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3979 /* No row means no WORKING node which mean we just fell off
3980 the WORKING tree, so CURRENT_RELPATH is the op-root
3981 closest to the wc root. */
3982 if (have_base && base_del_relpath)
3983 *base_del_relpath = apr_pstrdup(result_pool, current_relpath);
3987 /* Still in the WORKING tree so the first time we get here
3988 CURRENT_RELPATH is a delete op-root in the WORKING tree. */
3989 if (work_del_relpath && !*work_del_relpath)
3991 *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
3993 if (!scan && !base_del_relpath)
3994 break; /* We have all we need */
3997 current_relpath = parent_relpath;
3998 op_depth = svn_sqlite__column_int(stmt, 2);
3999 have_base = !svn_sqlite__column_is_null(stmt, 0);
4002 SVN_ERR(svn_sqlite__reset(stmt));
4004 return SVN_NO_ERROR;
4008 svn_wc__db_scan_deletion(const char **base_del_abspath,
4009 const char **moved_to_abspath,
4010 const char **work_del_abspath,
4011 const char **moved_to_op_root_abspath,
4013 const char *local_abspath,
4014 apr_pool_t *result_pool,
4015 apr_pool_t *scratch_pool)
4017 svn_wc__db_wcroot_t *wcroot;
4018 const char *local_relpath;
4019 const char *base_del_relpath, *moved_to_relpath, *work_del_relpath;
4020 const char *moved_to_op_root_relpath;
4022 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4024 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4025 local_abspath, scratch_pool, scratch_pool));
4026 VERIFY_USABLE_WCROOT(wcroot);
4028 SVN_WC__DB_WITH_TXN(
4029 scan_deletion_txn(&base_del_relpath, &moved_to_relpath,
4030 &work_del_relpath, &moved_to_op_root_relpath,
4031 wcroot, local_relpath, result_pool, scratch_pool),
4034 if (base_del_abspath)
4036 *base_del_abspath = (base_del_relpath
4037 ? svn_dirent_join(wcroot->abspath,
4038 base_del_relpath, result_pool)
4041 if (moved_to_abspath)
4043 *moved_to_abspath = (moved_to_relpath
4044 ? svn_dirent_join(wcroot->abspath,
4045 moved_to_relpath, result_pool)
4048 if (work_del_abspath)
4050 *work_del_abspath = (work_del_relpath
4051 ? svn_dirent_join(wcroot->abspath,
4052 work_del_relpath, result_pool)
4055 if (moved_to_op_root_abspath)
4057 *moved_to_op_root_abspath = (moved_to_op_root_relpath
4058 ? svn_dirent_join(wcroot->abspath,
4059 moved_to_op_root_relpath,
4064 return SVN_NO_ERROR;
4068 /* Set *COPYFROM_ID, *COPYFROM_RELPATH, *COPYFROM_REV to the values
4069 appropriate for the copy. Also return *STATUS, *KIND and *HAVE_WORK, *OP_ROOT
4070 since they are available. This is a helper for
4071 svn_wc__db_op_copy. */
4072 static svn_error_t *
4073 get_info_for_copy(apr_int64_t *copyfrom_id,
4074 const char **copyfrom_relpath,
4075 svn_revnum_t *copyfrom_rev,
4076 svn_wc__db_status_t *status,
4077 svn_node_kind_t *kind,
4078 svn_boolean_t *op_root,
4079 svn_wc__db_wcroot_t *wcroot,
4080 const char *local_relpath,
4081 apr_pool_t *result_pool,
4082 apr_pool_t *scratch_pool)
4084 const char *repos_relpath;
4085 svn_revnum_t revision;
4086 svn_wc__db_status_t node_status;
4087 apr_int64_t repos_id;
4088 svn_boolean_t is_op_root;
4090 SVN_ERR(read_info(&node_status, kind, &revision, &repos_relpath, &repos_id,
4091 NULL, NULL, NULL, NULL, NULL, NULL, copyfrom_relpath,
4092 copyfrom_id, copyfrom_rev, NULL, NULL, NULL, NULL,
4093 NULL, &is_op_root, NULL, NULL,
4094 NULL /* have_base */,
4095 NULL /* have_more_work */,
4096 NULL /* have_work */,
4097 wcroot, local_relpath, result_pool, scratch_pool));
4100 *op_root = is_op_root;
4102 if (node_status == svn_wc__db_status_excluded)
4104 /* The parent cannot be excluded, so look at the parent and then
4105 adjust the relpath */
4106 const char *parent_relpath, *base_name;
4108 svn_dirent_split(&parent_relpath, &base_name, local_relpath,
4110 SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev,
4112 wcroot, parent_relpath,
4113 scratch_pool, scratch_pool));
4114 if (*copyfrom_relpath)
4115 *copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, base_name,
4118 else if (node_status == svn_wc__db_status_added)
4120 SVN_ERR(scan_addition(&node_status, NULL, NULL, NULL, NULL, NULL, NULL,
4121 NULL, NULL, NULL, wcroot, local_relpath,
4122 scratch_pool, scratch_pool));
4124 else if (node_status == svn_wc__db_status_deleted && is_op_root)
4126 const char *base_del_relpath, *work_del_relpath;
4128 SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
4130 NULL, wcroot, local_relpath,
4131 scratch_pool, scratch_pool));
4132 if (work_del_relpath)
4134 const char *op_root_relpath;
4135 const char *parent_del_relpath = svn_relpath_dirname(work_del_relpath,
4138 /* Similar to, but not the same as, the _scan_addition and
4139 _join above. Can we use get_copyfrom here? */
4140 SVN_ERR(scan_addition(NULL, &op_root_relpath,
4141 NULL, NULL, /* repos_* */
4142 copyfrom_relpath, copyfrom_id, copyfrom_rev,
4143 NULL, NULL, NULL, wcroot, parent_del_relpath,
4144 scratch_pool, scratch_pool));
4146 = svn_relpath_join(*copyfrom_relpath,
4147 svn_relpath_skip_ancestor(op_root_relpath,
4151 else if (base_del_relpath)
4153 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, copyfrom_rev,
4155 copyfrom_id, NULL, NULL,
4156 NULL, NULL, NULL, NULL,
4157 NULL, NULL, NULL, NULL,
4158 wcroot, local_relpath,
4163 SVN_ERR_MALFUNCTION();
4165 else if (node_status == svn_wc__db_status_deleted)
4167 /* Keep original_* from read_info() to allow seeing the difference
4168 between base-deleted and not present */
4172 *copyfrom_relpath = repos_relpath;
4173 *copyfrom_rev = revision;
4174 *copyfrom_id = repos_id;
4178 *status = node_status;
4180 return SVN_NO_ERROR;
4184 /* Set *OP_DEPTH to the highest op depth of WCROOT:LOCAL_RELPATH. */
4185 static svn_error_t *
4186 op_depth_of(int *op_depth,
4187 svn_wc__db_wcroot_t *wcroot,
4188 const char *local_relpath)
4190 svn_sqlite__stmt_t *stmt;
4191 svn_boolean_t have_row;
4193 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4194 STMT_SELECT_NODE_INFO));
4195 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4196 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4197 SVN_ERR_ASSERT(have_row);
4198 *op_depth = svn_sqlite__column_int(stmt, 0);
4199 SVN_ERR(svn_sqlite__reset(stmt));
4201 return SVN_NO_ERROR;
4205 /* Determine at which OP_DEPTH a copy of COPYFROM_REPOS_ID, COPYFROM_RELPATH at
4206 revision COPYFROM_REVISION should be inserted as LOCAL_RELPATH. Do this
4207 by checking if this would be a direct child of a copy of its parent
4208 directory. If it is then set *OP_DEPTH to the op_depth of its parent.
4210 If the node is not a direct copy at the same revision of the parent
4211 *NP_OP_DEPTH will be set to the op_depth of the parent when a not-present
4212 node should be inserted at this op_depth. This will be the case when the
4213 parent already defined an incomplete child with the same name. Otherwise
4214 *NP_OP_DEPTH will be set to -1.
4216 If the parent node is not the parent of the to be copied node, then
4217 *OP_DEPTH will be set to the proper op_depth for a new operation root.
4219 Set *PARENT_OP_DEPTH to the op_depth of the parent.
4222 static svn_error_t *
4223 op_depth_for_copy(int *op_depth,
4225 int *parent_op_depth,
4226 apr_int64_t copyfrom_repos_id,
4227 const char *copyfrom_relpath,
4228 svn_revnum_t copyfrom_revision,
4229 svn_wc__db_wcroot_t *wcroot,
4230 const char *local_relpath,
4231 apr_pool_t *scratch_pool)
4233 const char *parent_relpath, *name;
4234 svn_sqlite__stmt_t *stmt;
4235 svn_boolean_t have_row;
4236 int incomplete_op_depth = -1;
4237 int min_op_depth = 1; /* Never touch BASE */
4239 *op_depth = relpath_depth(local_relpath);
4242 svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool);
4243 *parent_op_depth = relpath_depth(parent_relpath);
4245 if (!copyfrom_relpath)
4246 return SVN_NO_ERROR;
4248 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4249 STMT_SELECT_WORKING_NODE));
4250 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4251 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4254 svn_wc__db_status_t status = svn_sqlite__column_token(stmt, 1,
4257 min_op_depth = svn_sqlite__column_int(stmt, 0);
4258 if (status == svn_wc__db_status_incomplete)
4259 incomplete_op_depth = min_op_depth;
4261 SVN_ERR(svn_sqlite__reset(stmt));
4263 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4264 STMT_SELECT_WORKING_NODE));
4265 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4266 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4269 svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 1,
4272 *parent_op_depth = svn_sqlite__column_int(stmt, 0);
4273 if (*parent_op_depth < min_op_depth)
4275 /* We want to create a copy; not overwrite the lower layers */
4276 SVN_ERR(svn_sqlite__reset(stmt));
4277 return SVN_NO_ERROR;
4280 /* You can only add children below a node that exists.
4281 In WORKING that must be status added, which is represented
4282 as presence normal */
4283 SVN_ERR_ASSERT(presence == svn_wc__db_status_normal);
4285 if ((incomplete_op_depth < 0)
4286 || (incomplete_op_depth == *parent_op_depth))
4288 apr_int64_t parent_copyfrom_repos_id
4289 = svn_sqlite__column_int64(stmt, 10);
4290 const char *parent_copyfrom_relpath
4291 = svn_sqlite__column_text(stmt, 11, NULL);
4292 svn_revnum_t parent_copyfrom_revision
4293 = svn_sqlite__column_revnum(stmt, 12);
4295 if (parent_copyfrom_repos_id == copyfrom_repos_id)
4297 if (copyfrom_revision == parent_copyfrom_revision
4298 && !strcmp(copyfrom_relpath,
4299 svn_relpath_join(parent_copyfrom_relpath, name,
4301 *op_depth = *parent_op_depth;
4302 else if (incomplete_op_depth > 0)
4303 *np_op_depth = incomplete_op_depth;
4307 SVN_ERR(svn_sqlite__reset(stmt));
4309 return SVN_NO_ERROR;
4313 /* Like svn_wc__db_op_copy(), but with WCROOT+LOCAL_RELPATH
4314 * instead of DB+LOCAL_ABSPATH. A non-zero MOVE_OP_DEPTH implies that the
4315 * copy operation is part of a move, and indicates the op-depth of the
4316 * move destination op-root. */
4317 static svn_error_t *
4318 db_op_copy(svn_wc__db_wcroot_t *src_wcroot,
4319 const char *src_relpath,
4320 svn_wc__db_wcroot_t *dst_wcroot,
4321 const char *dst_relpath,
4322 const svn_skel_t *work_items,
4324 apr_pool_t *scratch_pool)
4326 const char *copyfrom_relpath;
4327 svn_revnum_t copyfrom_rev;
4328 svn_wc__db_status_t status;
4329 svn_wc__db_status_t dst_presence;
4330 svn_boolean_t op_root;
4331 apr_int64_t copyfrom_id;
4333 int dst_np_op_depth;
4334 int dst_parent_op_depth;
4335 svn_node_kind_t kind;
4336 const apr_array_header_t *children;
4338 SVN_ERR(get_info_for_copy(©from_id, ©from_relpath, ©from_rev,
4339 &status, &kind, &op_root, src_wcroot,
4340 src_relpath, scratch_pool, scratch_pool));
4342 SVN_ERR(op_depth_for_copy(&dst_op_depth, &dst_np_op_depth,
4343 &dst_parent_op_depth,
4344 copyfrom_id, copyfrom_relpath, copyfrom_rev,
4345 dst_wcroot, dst_relpath, scratch_pool));
4347 SVN_ERR_ASSERT(kind == svn_node_file || kind == svn_node_dir);
4349 /* ### New status, not finished, see notes/wc-ng/copying */
4352 case svn_wc__db_status_normal:
4353 case svn_wc__db_status_added:
4354 case svn_wc__db_status_moved_here:
4355 case svn_wc__db_status_copied:
4356 dst_presence = svn_wc__db_status_normal;
4358 case svn_wc__db_status_deleted:
4361 /* If the lower layer is already shadowcopied we can skip adding
4362 a not present node. */
4364 svn_wc__db_status_t dst_status;
4366 err = read_info(&dst_status, NULL, NULL, NULL, NULL, NULL, NULL,
4367 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4368 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4369 dst_wcroot, dst_relpath, scratch_pool, scratch_pool);
4373 if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
4374 svn_error_clear(err);
4376 return svn_error_trace(err);
4378 else if (dst_status == svn_wc__db_status_deleted)
4380 /* Node is already deleted; skip the NODES work, but do
4381 install wq items if requested */
4382 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4384 return SVN_NO_ERROR;
4389 /* This node is either a not-present node (which should be copied), or
4390 a base-delete of some lower layer (which shouldn't).
4391 Subversion <= 1.7 always added a not-present node here, which is
4392 safe (as it postpones the hard work until commit time and then we
4393 ask the repository), but it breaks some move scenarios.
4396 if (! copyfrom_relpath)
4398 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4400 return SVN_NO_ERROR;
4403 /* Fall through. Install not present node */
4405 case svn_wc__db_status_not_present:
4406 case svn_wc__db_status_excluded:
4407 /* These presence values should not create a new op depth */
4408 if (dst_np_op_depth > 0)
4410 dst_op_depth = dst_np_op_depth;
4411 dst_np_op_depth = -1;
4413 if (status == svn_wc__db_status_excluded)
4414 dst_presence = svn_wc__db_status_excluded;
4416 dst_presence = svn_wc__db_status_not_present;
4418 case svn_wc__db_status_server_excluded:
4419 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4420 _("Cannot copy '%s' excluded by server"),
4421 path_for_error_message(src_wcroot,
4425 /* Perhaps we should allow incomplete to incomplete? We can't
4426 avoid incomplete working nodes as one step in copying a
4427 directory is to add incomplete children. */
4428 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4429 _("Cannot handle status of '%s'"),
4430 path_for_error_message(src_wcroot,
4435 if (kind == svn_node_dir)
4439 SVN_ERR(op_depth_of(&src_op_depth, src_wcroot, src_relpath));
4440 SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
4441 src_op_depth, scratch_pool, scratch_pool));
4446 if (src_wcroot == dst_wcroot)
4448 svn_sqlite__stmt_t *stmt;
4449 const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
4452 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
4453 STMT_INSERT_WORKING_NODE_COPY_FROM));
4455 SVN_ERR(svn_sqlite__bindf(stmt, "issdst",
4456 src_wcroot->wc_id, src_relpath,
4460 presence_map, dst_presence));
4462 if (move_op_depth > 0)
4464 if (relpath_depth(dst_relpath) == move_op_depth)
4466 /* We're moving the root of the move operation.
4468 * When an added node or the op-root of a copy is moved,
4469 * there is no 'moved-from' corresponding to the moved-here
4470 * node. So the net effect is the same as copy+delete.
4471 * Perform a normal copy operation in these cases. */
4472 if (!(status == svn_wc__db_status_added ||
4473 (status == svn_wc__db_status_copied && op_root)))
4474 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4478 svn_sqlite__stmt_t *info_stmt;
4479 svn_boolean_t have_row;
4481 /* We're moving a child along with the root of the move.
4483 * Set moved-here depending on dst_parent, propagating the
4484 * above decision to moved-along children at the same op_depth.
4485 * We can't use scan_addition() to detect moved-here because
4486 * the delete-half of the move might not yet exist. */
4487 SVN_ERR(svn_sqlite__get_statement(&info_stmt, dst_wcroot->sdb,
4488 STMT_SELECT_NODE_INFO));
4489 SVN_ERR(svn_sqlite__bindf(info_stmt, "is", dst_wcroot->wc_id,
4490 dst_parent_relpath));
4491 SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4492 SVN_ERR_ASSERT(have_row);
4493 if (svn_sqlite__column_boolean(info_stmt, 15) &&
4494 dst_op_depth == dst_parent_op_depth)
4496 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4497 SVN_ERR(svn_sqlite__reset(info_stmt));
4501 SVN_ERR(svn_sqlite__reset(info_stmt));
4503 /* If the child has been moved into the tree we're moving,
4504 * keep its moved-here bit set. */
4505 SVN_ERR(svn_sqlite__get_statement(&info_stmt,
4507 STMT_SELECT_NODE_INFO));
4508 SVN_ERR(svn_sqlite__bindf(info_stmt, "is",
4509 dst_wcroot->wc_id, src_relpath));
4510 SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4511 SVN_ERR_ASSERT(have_row);
4512 if (svn_sqlite__column_boolean(info_stmt, 15))
4513 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4514 SVN_ERR(svn_sqlite__reset(info_stmt));
4519 SVN_ERR(svn_sqlite__step_done(stmt));
4521 /* ### Copying changelist is OK for a move but what about a copy? */
4522 SVN_ERR(copy_actual(src_wcroot, src_relpath,
4523 dst_wcroot, dst_relpath, scratch_pool));
4525 if (dst_np_op_depth > 0)
4527 /* We introduce a not-present node at the parent's op_depth to
4528 properly start a new op-depth at our own op_depth. This marks
4529 us as an op_root for commit and allows reverting just this
4532 SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
4534 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
4535 src_wcroot->wc_id, dst_relpath,
4536 dst_np_op_depth, dst_parent_relpath,
4537 copyfrom_id, copyfrom_relpath,
4540 svn_wc__db_status_not_present,
4544 SVN_ERR(svn_sqlite__step_done(stmt));
4546 /* Insert incomplete children, if relevant.
4547 The children are part of the same op and so have the same op_depth.
4548 (The only time we'd want a different depth is during a recursive
4549 simple add, but we never insert children here during a simple add.) */
4550 if (kind == svn_node_dir
4551 && dst_presence == svn_wc__db_status_normal)
4552 SVN_ERR(insert_incomplete_children(
4565 if (copyfrom_relpath)
4567 const char *repos_root_url;
4568 const char *repos_uuid;
4570 /* Pass the right repos-id for the destination db! */
4572 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
4573 src_wcroot->sdb, copyfrom_id,
4576 SVN_ERR(create_repos_id(©from_id, repos_root_url, repos_uuid,
4577 dst_wcroot->sdb, scratch_pool));
4580 SVN_ERR(cross_db_copy(src_wcroot, src_relpath, dst_wcroot,
4581 dst_relpath, dst_presence, dst_op_depth,
4582 dst_np_op_depth, kind,
4583 children, copyfrom_id, copyfrom_relpath,
4584 copyfrom_rev, scratch_pool));
4587 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items, scratch_pool));
4589 return SVN_NO_ERROR;
4592 /* Baton for passing args to op_copy_txn(). */
4593 struct op_copy_baton
4595 svn_wc__db_wcroot_t *src_wcroot;
4596 const char *src_relpath;
4598 svn_wc__db_wcroot_t *dst_wcroot;
4599 const char *dst_relpath;
4601 const svn_skel_t *work_items;
4603 svn_boolean_t is_move;
4604 const char *dst_op_root_relpath;
4607 /* Helper for svn_wc__db_op_copy().
4609 * Implements svn_sqlite__transaction_callback_t. */
4610 static svn_error_t *
4611 op_copy_txn(void * baton,
4612 svn_sqlite__db_t *sdb,
4613 apr_pool_t *scratch_pool)
4615 struct op_copy_baton *ocb = baton;
4618 if (sdb != ocb->dst_wcroot->sdb)
4620 /* Source and destination databases differ; so also start a lock
4621 in the destination database, by calling ourself in a lock. */
4623 return svn_error_trace(
4624 svn_sqlite__with_lock(ocb->dst_wcroot->sdb,
4625 op_copy_txn, ocb, scratch_pool));
4628 /* From this point we can assume a lock in the src and dst databases */
4631 move_op_depth = relpath_depth(ocb->dst_op_root_relpath);
4635 SVN_ERR(db_op_copy(ocb->src_wcroot, ocb->src_relpath,
4636 ocb->dst_wcroot, ocb->dst_relpath,
4637 ocb->work_items, move_op_depth, scratch_pool));
4639 return SVN_NO_ERROR;
4643 svn_wc__db_op_copy(svn_wc__db_t *db,
4644 const char *src_abspath,
4645 const char *dst_abspath,
4646 const char *dst_op_root_abspath,
4647 svn_boolean_t is_move,
4648 const svn_skel_t *work_items,
4649 apr_pool_t *scratch_pool)
4651 struct op_copy_baton ocb = {0};
4653 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
4654 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
4655 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_op_root_abspath));
4657 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
4658 &ocb.src_relpath, db,
4660 scratch_pool, scratch_pool));
4661 VERIFY_USABLE_WCROOT(ocb.src_wcroot);
4663 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
4666 scratch_pool, scratch_pool));
4667 VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
4669 ocb.work_items = work_items;
4670 ocb.is_move = is_move;
4671 ocb.dst_op_root_relpath = svn_dirent_skip_ancestor(ocb.dst_wcroot->abspath,
4672 dst_op_root_abspath);
4674 /* Call with the sdb in src_wcroot. It might call itself again to
4675 also obtain a lock in dst_wcroot */
4676 SVN_ERR(svn_sqlite__with_lock(ocb.src_wcroot->sdb, op_copy_txn, &ocb,
4679 return SVN_NO_ERROR;
4682 /* The txn body of svn_wc__db_op_handle_move_back */
4683 static svn_error_t *
4684 handle_move_back(svn_boolean_t *moved_back,
4685 svn_wc__db_wcroot_t *wcroot,
4686 const char *local_relpath,
4687 const char *moved_from_relpath,
4688 const svn_skel_t *work_items,
4689 apr_pool_t *scratch_pool)
4691 svn_sqlite__stmt_t *stmt;
4692 svn_wc__db_status_t status;
4693 svn_boolean_t op_root;
4694 svn_boolean_t have_more_work;
4695 int from_op_depth = 0;
4696 svn_boolean_t have_row;
4697 svn_boolean_t different = FALSE;
4699 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
4701 SVN_ERR(svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL, NULL,
4702 NULL, NULL, NULL, NULL, NULL, NULL,
4703 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4704 &op_root, NULL, NULL, NULL,
4705 &have_more_work, NULL,
4706 wcroot, local_relpath,
4707 scratch_pool, scratch_pool));
4709 if (status != svn_wc__db_status_added || !op_root)
4710 return SVN_NO_ERROR;
4712 /* We have two cases here: BASE-move-back and WORKING-move-back */
4714 SVN_ERR(op_depth_of(&from_op_depth, wcroot,
4715 svn_relpath_dirname(local_relpath, scratch_pool)));
4719 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4720 STMT_SELECT_MOVED_BACK));
4722 SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id,
4725 relpath_depth(local_relpath)));
4727 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4729 SVN_ERR_ASSERT(have_row); /* We checked that the node is an op-root */
4732 svn_boolean_t moved_here = svn_sqlite__column_boolean(stmt, 9);
4733 const char *moved_to = svn_sqlite__column_text(stmt, 10, NULL);
4737 || strcmp(moved_to, moved_from_relpath))
4746 svn_wc__db_status_t upper_status;
4747 svn_wc__db_status_t lower_status;
4749 upper_status = svn_sqlite__column_token(stmt, 1, presence_map);
4751 if (svn_sqlite__column_is_null(stmt, 5))
4753 /* No lower layer replaced. */
4754 if (upper_status != svn_wc__db_status_not_present)
4762 lower_status = svn_sqlite__column_token(stmt, 5, presence_map);
4764 if (upper_status != lower_status)
4770 if (upper_status == svn_wc__db_status_not_present
4771 || upper_status == svn_wc__db_status_excluded)
4773 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4774 continue; /* Nothing to check */
4776 else if (upper_status != svn_wc__db_status_normal)
4778 /* Not a normal move. Mixed revision move? */
4784 const char *upper_repos_relpath;
4785 const char *lower_repos_relpath;
4787 upper_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL);
4788 lower_repos_relpath = svn_sqlite__column_text(stmt, 7, NULL);
4790 if (! upper_repos_relpath
4791 || strcmp(upper_repos_relpath, lower_repos_relpath))
4799 svn_revnum_t upper_rev;
4800 svn_revnum_t lower_rev;
4802 upper_rev = svn_sqlite__column_revnum(stmt, 4);
4803 lower_rev = svn_sqlite__column_revnum(stmt, 8);
4805 if (upper_rev != lower_rev)
4813 apr_int64_t upper_repos_id;
4814 apr_int64_t lower_repos_id;
4816 upper_repos_id = svn_sqlite__column_int64(stmt, 2);
4817 lower_repos_id = svn_sqlite__column_int64(stmt, 6);
4819 if (upper_repos_id != lower_repos_id)
4826 /* Check moved_here? */
4828 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4830 SVN_ERR(svn_sqlite__reset(stmt));
4834 /* Ok, we can now safely remove this complete move, because we
4835 determined that it 100% matches the layer below it. */
4837 /* ### We could copy the recorded timestamps from the higher to the
4838 lower layer in an attempt to improve status performance, but
4839 generally these values should be the same anyway as it was
4841 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4842 STMT_DELETE_MOVED_BACK));
4844 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
4846 relpath_depth(local_relpath)));
4848 SVN_ERR(svn_sqlite__step_done(stmt));
4854 return SVN_NO_ERROR;
4858 svn_wc__db_op_handle_move_back(svn_boolean_t *moved_back,
4860 const char *local_abspath,
4861 const char *moved_from_abspath,
4862 const svn_skel_t *work_items,
4863 apr_pool_t *scratch_pool)
4865 svn_wc__db_wcroot_t *wcroot;
4866 const char *local_relpath;
4867 const char *moved_from_relpath;
4868 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4870 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4872 scratch_pool, scratch_pool));
4873 VERIFY_USABLE_WCROOT(wcroot);
4876 *moved_back = FALSE;
4878 moved_from_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
4879 moved_from_abspath);
4881 if (! local_relpath[0]
4882 || !moved_from_relpath)
4884 /* WC-Roots can't be moved */
4885 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
4886 return SVN_NO_ERROR;
4889 SVN_WC__DB_WITH_TXN(handle_move_back(moved_back, wcroot, local_relpath,
4890 moved_from_relpath, work_items,
4894 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
4897 return SVN_NO_ERROR;
4901 /* The recursive implementation of svn_wc__db_op_copy_shadowed_layer.
4903 * A non-zero MOVE_OP_DEPTH implies that the copy operation is part of
4904 * a move, and indicates the op-depth of the move destination op-root. */
4905 static svn_error_t *
4906 db_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot,
4907 const char *src_relpath,
4909 svn_wc__db_wcroot_t *dst_wcroot,
4910 const char *dst_relpath,
4913 apr_int64_t repos_id,
4914 const char *repos_relpath,
4915 svn_revnum_t revision,
4917 apr_pool_t *scratch_pool)
4919 const apr_array_header_t *children;
4920 apr_pool_t *iterpool;
4921 svn_wc__db_status_t status;
4922 svn_node_kind_t kind;
4923 svn_revnum_t node_revision;
4924 const char *node_repos_relpath;
4925 apr_int64_t node_repos_id;
4926 svn_sqlite__stmt_t *stmt;
4927 svn_wc__db_status_t dst_presence;
4932 err = svn_wc__db_depth_get_info(&status, &kind, &node_revision,
4933 &node_repos_relpath, &node_repos_id,
4934 NULL, NULL, NULL, NULL, NULL, NULL,
4936 src_wcroot, src_relpath, src_op_depth,
4937 scratch_pool, scratch_pool);
4941 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
4942 return svn_error_trace(err);
4944 svn_error_clear(err);
4945 return SVN_NO_ERROR; /* There is no shadowed node at src_op_depth */
4949 if (src_op_depth == 0)
4951 /* If the node is switched or has a different revision then its parent
4952 we shouldn't copy it. (We can't as we would have to insert it at
4953 an unshadowed depth) */
4954 if (status == svn_wc__db_status_not_present
4955 || status == svn_wc__db_status_excluded
4956 || status == svn_wc__db_status_server_excluded
4957 || node_revision != revision
4958 || node_repos_id != repos_id
4959 || strcmp(node_repos_relpath, repos_relpath))
4961 /* Add a not-present node in the destination wcroot */
4962 struct insert_working_baton_t iwb;
4963 const char *repos_root_url;
4964 const char *repos_uuid;
4966 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
4967 src_wcroot->sdb, node_repos_id,
4970 SVN_ERR(create_repos_id(&node_repos_id, repos_root_url, repos_uuid,
4971 dst_wcroot->sdb, scratch_pool));
4975 iwb.op_depth = dst_op_depth;
4976 if (status != svn_wc__db_status_excluded)
4977 iwb.presence = svn_wc__db_status_not_present;
4979 iwb.presence = svn_wc__db_status_excluded;
4983 iwb.original_repos_id = node_repos_id;
4984 iwb.original_revnum = node_revision;
4985 iwb.original_repos_relpath = node_repos_relpath;
4987 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
4990 return SVN_NO_ERROR;
4994 iterpool = svn_pool_create(scratch_pool);
4998 case svn_wc__db_status_normal:
4999 case svn_wc__db_status_added:
5000 case svn_wc__db_status_moved_here:
5001 case svn_wc__db_status_copied:
5002 dst_presence = svn_wc__db_status_normal;
5004 case svn_wc__db_status_deleted:
5005 case svn_wc__db_status_not_present:
5006 dst_presence = svn_wc__db_status_not_present;
5008 case svn_wc__db_status_excluded:
5009 dst_presence = svn_wc__db_status_excluded;
5011 case svn_wc__db_status_server_excluded:
5012 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5013 _("Cannot copy '%s' excluded by server"),
5014 path_for_error_message(src_wcroot,
5018 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5019 _("Cannot handle status of '%s'"),
5020 path_for_error_message(src_wcroot,
5025 if (dst_presence == svn_wc__db_status_normal
5026 && src_wcroot == dst_wcroot) /* ### Remove limitation */
5028 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
5029 STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH));
5031 SVN_ERR(svn_sqlite__bindf(stmt, "issdstd",
5032 src_wcroot->wc_id, src_relpath,
5035 svn_relpath_dirname(dst_relpath, iterpool),
5036 presence_map, dst_presence,
5040 if (dst_op_depth == move_op_depth)
5041 SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
5043 SVN_ERR(svn_sqlite__step_done(stmt));
5046 /* And mark it deleted to allow proper shadowing */
5047 struct insert_working_baton_t iwb;
5051 iwb.op_depth = del_op_depth;
5052 iwb.presence = svn_wc__db_status_base_deleted;
5056 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5062 struct insert_working_baton_t iwb;
5063 if (dst_presence == svn_wc__db_status_normal) /* Fallback for multi-db */
5064 dst_presence = svn_wc__db_status_not_present;
5066 /* And mark it deleted to allow proper shadowing */
5070 iwb.op_depth = dst_op_depth;
5071 iwb.presence = dst_presence;
5074 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5078 SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
5079 src_op_depth, scratch_pool, iterpool));
5081 for (i = 0; i < children->nelts; i++)
5083 const char *name = APR_ARRAY_IDX(children, i, const char *);
5084 const char *child_src_relpath;
5085 const char *child_dst_relpath;
5086 const char *child_repos_relpath = NULL;
5088 svn_pool_clear(iterpool);
5089 child_src_relpath = svn_relpath_join(src_relpath, name, iterpool);
5090 child_dst_relpath = svn_relpath_join(dst_relpath, name, iterpool);
5093 child_repos_relpath = svn_relpath_join(repos_relpath, name, iterpool);
5095 SVN_ERR(db_op_copy_shadowed_layer(
5096 src_wcroot, child_src_relpath, src_op_depth,
5097 dst_wcroot, child_dst_relpath, dst_op_depth,
5099 repos_id, child_repos_relpath, revision,
5100 move_op_depth, scratch_pool));
5103 svn_pool_destroy(iterpool);
5105 return SVN_NO_ERROR;
5108 /* Helper for svn_wc__db_op_copy_shadowed_layer().
5110 * Implements svn_sqlite__transaction_callback_t. */
5111 static svn_error_t *
5112 op_copy_shadowed_layer_txn(void *baton,
5113 svn_sqlite__db_t *sdb,
5114 apr_pool_t *scratch_pool)
5116 struct op_copy_baton *ocb = baton;
5117 const char *src_parent_relpath;
5118 const char *dst_parent_relpath;
5122 const char *repos_relpath = NULL;
5123 apr_int64_t repos_id = INVALID_REPOS_ID;
5124 svn_revnum_t revision = SVN_INVALID_REVNUM;
5126 if (sdb != ocb->dst_wcroot->sdb)
5128 /* Source and destination databases differ; so also start a lock
5129 in the destination database, by calling ourself in a lock. */
5131 return svn_error_trace(
5132 svn_sqlite__with_lock(ocb->dst_wcroot->sdb,
5133 op_copy_shadowed_layer_txn,
5134 ocb, scratch_pool));
5137 /* From this point we can assume a lock in the src and dst databases */
5140 /* src_relpath and dst_relpath can't be wcroot as we need their parents */
5141 SVN_ERR_ASSERT(*ocb->src_relpath && *ocb->dst_relpath);
5143 src_parent_relpath = svn_relpath_dirname(ocb->src_relpath, scratch_pool);
5144 dst_parent_relpath = svn_relpath_dirname(ocb->dst_relpath, scratch_pool);
5146 /* src_parent must be status normal or added; get its op-depth */
5147 SVN_ERR(op_depth_of(&src_op_depth, ocb->src_wcroot, src_parent_relpath));
5149 /* dst_parent must be status added; get its op-depth */
5150 SVN_ERR(op_depth_of(&dst_op_depth, ocb->dst_wcroot, dst_parent_relpath));
5152 del_op_depth = relpath_depth(ocb->dst_relpath);
5154 /* Get some information from the parent */
5155 SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, &revision, &repos_relpath,
5156 &repos_id, NULL, NULL, NULL, NULL, NULL,
5159 src_parent_relpath, src_op_depth,
5160 scratch_pool, scratch_pool));
5162 if (repos_relpath == NULL)
5164 /* The node is a local addition and has no shadowed information */
5165 return SVN_NO_ERROR;
5168 /* And calculate the child repos relpath */
5169 repos_relpath = svn_relpath_join(repos_relpath,
5170 svn_relpath_basename(ocb->src_relpath,
5174 SVN_ERR(db_op_copy_shadowed_layer(
5175 ocb->src_wcroot, ocb->src_relpath, src_op_depth,
5176 ocb->dst_wcroot, ocb->dst_relpath, dst_op_depth,
5178 repos_id, repos_relpath, revision,
5179 (ocb->is_move ? dst_op_depth : 0),
5182 return SVN_NO_ERROR;
5186 svn_wc__db_op_copy_shadowed_layer(svn_wc__db_t *db,
5187 const char *src_abspath,
5188 const char *dst_abspath,
5189 svn_boolean_t is_move,
5190 apr_pool_t *scratch_pool)
5192 struct op_copy_baton ocb = {0};
5194 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
5195 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
5197 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
5198 &ocb.src_relpath, db,
5200 scratch_pool, scratch_pool));
5201 VERIFY_USABLE_WCROOT(ocb.src_wcroot);
5203 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
5206 scratch_pool, scratch_pool));
5207 VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
5209 ocb.is_move = is_move;
5210 ocb.dst_op_root_relpath = NULL; /* not used by op_copy_shadowed_layer_txn */
5212 ocb.work_items = NULL;
5214 /* Call with the sdb in src_wcroot. It might call itself again to
5215 also obtain a lock in dst_wcroot */
5216 SVN_ERR(svn_sqlite__with_lock(ocb.src_wcroot->sdb,
5217 op_copy_shadowed_layer_txn,
5218 &ocb, scratch_pool));
5220 return SVN_NO_ERROR;
5224 /* If there are any server-excluded base nodes then the copy must fail
5225 as it's not possible to commit such a copy.
5226 Return an error if there are any server-excluded nodes. */
5227 static svn_error_t *
5228 catch_copy_of_server_excluded(svn_wc__db_wcroot_t *wcroot,
5229 const char *local_relpath,
5230 apr_pool_t *scratch_pool)
5232 svn_sqlite__stmt_t *stmt;
5233 svn_boolean_t have_row;
5234 const char *server_excluded_relpath;
5236 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5237 STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
5238 SVN_ERR(svn_sqlite__bindf(stmt, "is",
5241 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5243 server_excluded_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
5244 SVN_ERR(svn_sqlite__reset(stmt));
5246 return svn_error_createf(SVN_ERR_AUTHZ_UNREADABLE, NULL,
5247 _("Cannot copy '%s' excluded by server"),
5248 path_for_error_message(wcroot,
5249 server_excluded_relpath,
5252 return SVN_NO_ERROR;
5257 svn_wc__db_op_copy_dir(svn_wc__db_t *db,
5258 const char *local_abspath,
5259 const apr_hash_t *props,
5260 svn_revnum_t changed_rev,
5261 apr_time_t changed_date,
5262 const char *changed_author,
5263 const char *original_repos_relpath,
5264 const char *original_root_url,
5265 const char *original_uuid,
5266 svn_revnum_t original_revision,
5267 const apr_array_header_t *children,
5268 svn_boolean_t is_move,
5270 const svn_skel_t *conflict,
5271 const svn_skel_t *work_items,
5272 apr_pool_t *scratch_pool)
5274 svn_wc__db_wcroot_t *wcroot;
5275 const char *local_relpath;
5276 insert_working_baton_t iwb;
5277 int parent_op_depth;
5279 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5280 SVN_ERR_ASSERT(props != NULL);
5281 /* ### any assertions for CHANGED_* ? */
5282 /* ### any assertions for ORIGINAL_* ? */
5284 SVN_ERR_ASSERT(children != NULL);
5287 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5288 local_abspath, scratch_pool, scratch_pool));
5289 VERIFY_USABLE_WCROOT(wcroot);
5293 iwb.presence = svn_wc__db_status_normal;
5294 iwb.kind = svn_node_dir;
5297 iwb.changed_rev = changed_rev;
5298 iwb.changed_date = changed_date;
5299 iwb.changed_author = changed_author;
5301 if (original_root_url != NULL)
5303 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5304 original_root_url, original_uuid,
5305 wcroot->sdb, scratch_pool));
5306 iwb.original_repos_relpath = original_repos_relpath;
5307 iwb.original_revnum = original_revision;
5310 /* ### Should we do this inside the transaction? */
5311 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5312 &parent_op_depth, iwb.original_repos_id,
5313 original_repos_relpath, original_revision,
5314 wcroot, local_relpath, scratch_pool));
5316 iwb.children = children;
5318 iwb.moved_here = is_move && (parent_op_depth == 0 ||
5319 iwb.op_depth == parent_op_depth);
5321 iwb.work_items = work_items;
5322 iwb.conflict = conflict;
5324 SVN_WC__DB_WITH_TXN(
5325 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5327 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
5329 return SVN_NO_ERROR;
5334 svn_wc__db_op_copy_file(svn_wc__db_t *db,
5335 const char *local_abspath,
5336 const apr_hash_t *props,
5337 svn_revnum_t changed_rev,
5338 apr_time_t changed_date,
5339 const char *changed_author,
5340 const char *original_repos_relpath,
5341 const char *original_root_url,
5342 const char *original_uuid,
5343 svn_revnum_t original_revision,
5344 const svn_checksum_t *checksum,
5345 svn_boolean_t update_actual_props,
5346 const apr_hash_t *new_actual_props,
5347 svn_boolean_t is_move,
5348 const svn_skel_t *conflict,
5349 const svn_skel_t *work_items,
5350 apr_pool_t *scratch_pool)
5352 svn_wc__db_wcroot_t *wcroot;
5353 const char *local_relpath;
5354 insert_working_baton_t iwb;
5355 int parent_op_depth;
5357 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5358 SVN_ERR_ASSERT(props != NULL);
5359 /* ### any assertions for CHANGED_* ? */
5360 SVN_ERR_ASSERT((! original_repos_relpath && ! original_root_url
5361 && ! original_uuid && ! checksum
5362 && original_revision == SVN_INVALID_REVNUM)
5363 || (original_repos_relpath && original_root_url
5364 && original_uuid && checksum
5365 && original_revision != SVN_INVALID_REVNUM));
5367 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5368 local_abspath, scratch_pool, scratch_pool));
5369 VERIFY_USABLE_WCROOT(wcroot);
5373 iwb.presence = svn_wc__db_status_normal;
5374 iwb.kind = svn_node_file;
5377 iwb.changed_rev = changed_rev;
5378 iwb.changed_date = changed_date;
5379 iwb.changed_author = changed_author;
5381 if (original_root_url != NULL)
5383 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5384 original_root_url, original_uuid,
5385 wcroot->sdb, scratch_pool));
5386 iwb.original_repos_relpath = original_repos_relpath;
5387 iwb.original_revnum = original_revision;
5390 /* ### Should we do this inside the transaction? */
5391 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5392 &parent_op_depth, iwb.original_repos_id,
5393 original_repos_relpath, original_revision,
5394 wcroot, local_relpath, scratch_pool));
5396 iwb.checksum = checksum;
5397 iwb.moved_here = is_move && (parent_op_depth == 0 ||
5398 iwb.op_depth == parent_op_depth);
5400 if (update_actual_props)
5402 iwb.update_actual_props = update_actual_props;
5403 iwb.new_actual_props = new_actual_props;
5406 iwb.work_items = work_items;
5407 iwb.conflict = conflict;
5409 SVN_WC__DB_WITH_TXN(
5410 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5412 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5414 return SVN_NO_ERROR;
5419 svn_wc__db_op_copy_symlink(svn_wc__db_t *db,
5420 const char *local_abspath,
5421 const apr_hash_t *props,
5422 svn_revnum_t changed_rev,
5423 apr_time_t changed_date,
5424 const char *changed_author,
5425 const char *original_repos_relpath,
5426 const char *original_root_url,
5427 const char *original_uuid,
5428 svn_revnum_t original_revision,
5430 const svn_skel_t *conflict,
5431 const svn_skel_t *work_items,
5432 apr_pool_t *scratch_pool)
5434 svn_wc__db_wcroot_t *wcroot;
5435 const char *local_relpath;
5436 insert_working_baton_t iwb;
5437 int parent_op_depth;
5439 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5440 SVN_ERR_ASSERT(props != NULL);
5441 /* ### any assertions for CHANGED_* ? */
5442 /* ### any assertions for ORIGINAL_* ? */
5443 SVN_ERR_ASSERT(target != NULL);
5445 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5446 local_abspath, scratch_pool, scratch_pool));
5447 VERIFY_USABLE_WCROOT(wcroot);
5451 iwb.presence = svn_wc__db_status_normal;
5452 iwb.kind = svn_node_symlink;
5455 iwb.changed_rev = changed_rev;
5456 iwb.changed_date = changed_date;
5457 iwb.changed_author = changed_author;
5458 iwb.moved_here = FALSE;
5460 if (original_root_url != NULL)
5462 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5463 original_root_url, original_uuid,
5464 wcroot->sdb, scratch_pool));
5465 iwb.original_repos_relpath = original_repos_relpath;
5466 iwb.original_revnum = original_revision;
5469 /* ### Should we do this inside the transaction? */
5470 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5471 &parent_op_depth, iwb.original_repos_id,
5472 original_repos_relpath, original_revision,
5473 wcroot, local_relpath, scratch_pool));
5475 iwb.target = target;
5477 iwb.work_items = work_items;
5478 iwb.conflict = conflict;
5480 SVN_WC__DB_WITH_TXN(
5481 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5483 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5485 return SVN_NO_ERROR;
5490 svn_wc__db_op_add_directory(svn_wc__db_t *db,
5491 const char *local_abspath,
5492 const apr_hash_t *props,
5493 const svn_skel_t *work_items,
5494 apr_pool_t *scratch_pool)
5496 svn_wc__db_wcroot_t *wcroot;
5497 const char *local_relpath;
5498 const char *dir_abspath;
5500 insert_working_baton_t iwb;
5502 /* Resolve wcroot via parent directory to avoid obstruction handling */
5503 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5504 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5506 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5507 dir_abspath, scratch_pool, scratch_pool));
5508 VERIFY_USABLE_WCROOT(wcroot);
5512 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5513 iwb.presence = svn_wc__db_status_normal;
5514 iwb.kind = svn_node_dir;
5515 iwb.op_depth = relpath_depth(local_relpath);
5516 if (props && apr_hash_count((apr_hash_t *)props))
5518 iwb.update_actual_props = TRUE;
5519 iwb.new_actual_props = props;
5522 iwb.work_items = work_items;
5524 SVN_WC__DB_WITH_TXN(
5525 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5527 /* Use depth infinity to make sure we have no invalid cached information
5528 * about children of this dir. */
5529 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5532 return SVN_NO_ERROR;
5537 svn_wc__db_op_add_file(svn_wc__db_t *db,
5538 const char *local_abspath,
5539 const apr_hash_t *props,
5540 const svn_skel_t *work_items,
5541 apr_pool_t *scratch_pool)
5543 svn_wc__db_wcroot_t *wcroot;
5544 const char *local_relpath;
5545 insert_working_baton_t iwb;
5546 const char *dir_abspath;
5549 /* Resolve wcroot via parent directory to avoid obstruction handling */
5550 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5551 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5553 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5554 dir_abspath, scratch_pool, scratch_pool));
5555 VERIFY_USABLE_WCROOT(wcroot);
5559 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5560 iwb.presence = svn_wc__db_status_normal;
5561 iwb.kind = svn_node_file;
5562 iwb.op_depth = relpath_depth(local_relpath);
5563 if (props && apr_hash_count((apr_hash_t *)props))
5565 iwb.update_actual_props = TRUE;
5566 iwb.new_actual_props = props;
5569 iwb.work_items = work_items;
5571 SVN_WC__DB_WITH_TXN(
5572 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5574 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5576 return SVN_NO_ERROR;
5581 svn_wc__db_op_add_symlink(svn_wc__db_t *db,
5582 const char *local_abspath,
5584 const apr_hash_t *props,
5585 const svn_skel_t *work_items,
5586 apr_pool_t *scratch_pool)
5588 svn_wc__db_wcroot_t *wcroot;
5589 const char *local_relpath;
5590 insert_working_baton_t iwb;
5591 const char *dir_abspath;
5594 /* Resolve wcroot via parent directory to avoid obstruction handling */
5595 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5596 SVN_ERR_ASSERT(target != NULL);
5598 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5600 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5601 dir_abspath, scratch_pool, scratch_pool));
5603 VERIFY_USABLE_WCROOT(wcroot);
5607 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5608 iwb.presence = svn_wc__db_status_normal;
5609 iwb.kind = svn_node_symlink;
5610 iwb.op_depth = relpath_depth(local_relpath);
5611 if (props && apr_hash_count((apr_hash_t *)props))
5613 iwb.update_actual_props = TRUE;
5614 iwb.new_actual_props = props;
5617 iwb.target = target;
5619 iwb.work_items = work_items;
5621 SVN_WC__DB_WITH_TXN(
5622 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5624 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5626 return SVN_NO_ERROR;
5629 /* Record RECORDED_SIZE and RECORDED_TIME into top layer in NODES */
5630 static svn_error_t *
5631 db_record_fileinfo(svn_wc__db_wcroot_t *wcroot,
5632 const char *local_relpath,
5633 apr_int64_t recorded_size,
5634 apr_int64_t recorded_time,
5635 apr_pool_t *scratch_pool)
5637 svn_sqlite__stmt_t *stmt;
5640 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5641 STMT_UPDATE_NODE_FILEINFO));
5642 SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
5643 recorded_size, recorded_time));
5644 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
5646 SVN_ERR_ASSERT(affected_rows == 1);
5648 return SVN_NO_ERROR;
5653 svn_wc__db_global_record_fileinfo(svn_wc__db_t *db,
5654 const char *local_abspath,
5655 svn_filesize_t recorded_size,
5656 apr_time_t recorded_time,
5657 apr_pool_t *scratch_pool)
5659 svn_wc__db_wcroot_t *wcroot;
5660 const char *local_relpath;
5662 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5664 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5665 local_abspath, scratch_pool, scratch_pool));
5666 VERIFY_USABLE_WCROOT(wcroot);
5668 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
5669 recorded_size, recorded_time, scratch_pool));
5671 /* We *totally* monkeyed the entries. Toss 'em. */
5672 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5674 return SVN_NO_ERROR;
5678 /* Set the ACTUAL_NODE properties column for (WC_ID, LOCAL_RELPATH) to
5681 * Note: PROPS=NULL means the actual props are the same as the pristine
5682 * props; to indicate no properties when the pristine has some props,
5683 * PROPS must be an empty hash. */
5684 static svn_error_t *
5685 set_actual_props(apr_int64_t wc_id,
5686 const char *local_relpath,
5688 svn_sqlite__db_t *db,
5689 apr_pool_t *scratch_pool)
5691 svn_sqlite__stmt_t *stmt;
5694 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_UPDATE_ACTUAL_PROPS));
5695 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
5696 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
5697 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
5699 if (affected_rows == 1 || !props)
5700 return SVN_NO_ERROR; /* We are done */
5702 /* We have to insert a row in ACTUAL */
5704 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_ACTUAL_PROPS));
5705 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
5706 if (*local_relpath != '\0')
5707 SVN_ERR(svn_sqlite__bind_text(stmt, 3,
5708 svn_relpath_dirname(local_relpath,
5710 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
5711 return svn_error_trace(svn_sqlite__step_done(stmt));
5715 /* The body of svn_wc__db_op_set_props().
5717 Set the 'properties' column in the 'ACTUAL_NODE' table to BATON->props.
5718 Create an entry in the ACTUAL table for the node if it does not yet
5720 To specify no properties, BATON->props must be an empty hash, not NULL.
5721 BATON is of type 'struct set_props_baton_t'.
5723 static svn_error_t *
5724 set_props_txn(svn_wc__db_wcroot_t *wcroot,
5725 const char *local_relpath,
5727 svn_boolean_t clear_recorded_info,
5728 const svn_skel_t *conflict,
5729 const svn_skel_t *work_items,
5730 apr_pool_t *scratch_pool)
5732 apr_hash_t *pristine_props;
5734 /* Check if the props are modified. If no changes, then wipe out the
5735 ACTUAL props. PRISTINE_PROPS==NULL means that any
5736 ACTUAL props are okay as provided, so go ahead and set them. */
5737 SVN_ERR(db_read_pristine_props(&pristine_props, wcroot, local_relpath, FALSE,
5738 scratch_pool, scratch_pool));
5739 if (props && pristine_props)
5741 apr_array_header_t *prop_diffs;
5743 SVN_ERR(svn_prop_diffs(&prop_diffs, props, pristine_props,
5745 if (prop_diffs->nelts == 0)
5749 SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath,
5750 props, wcroot->sdb, scratch_pool));
5752 if (clear_recorded_info)
5754 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
5755 SVN_INVALID_FILESIZE, 0,
5760 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5762 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
5763 conflict, scratch_pool));
5765 return SVN_NO_ERROR;
5770 svn_wc__db_op_set_props(svn_wc__db_t *db,
5771 const char *local_abspath,
5773 svn_boolean_t clear_recorded_info,
5774 const svn_skel_t *conflict,
5775 const svn_skel_t *work_items,
5776 apr_pool_t *scratch_pool)
5778 svn_wc__db_wcroot_t *wcroot;
5779 const char *local_relpath;
5781 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5783 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
5784 db, local_abspath, scratch_pool, scratch_pool));
5785 VERIFY_USABLE_WCROOT(wcroot);
5787 SVN_WC__DB_WITH_TXN(set_props_txn(wcroot, local_relpath, props,
5788 clear_recorded_info, conflict, work_items,
5791 return SVN_NO_ERROR;
5796 svn_wc__db_op_modified(svn_wc__db_t *db,
5797 const char *local_abspath,
5798 apr_pool_t *scratch_pool)
5800 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5806 static svn_error_t *
5807 populate_targets_tree(svn_wc__db_wcroot_t *wcroot,
5808 const char *local_relpath,
5810 const apr_array_header_t *changelist_filter,
5811 apr_pool_t *scratch_pool)
5813 svn_sqlite__stmt_t *stmt;
5814 int affected_rows = 0;
5815 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
5816 STMT_CREATE_TARGETS_LIST));
5818 if (changelist_filter && changelist_filter->nelts > 0)
5820 /* Iterate over the changelists, adding the nodes which match.
5821 Common case: we only have one changelist, so this only
5828 case svn_depth_empty:
5829 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST;
5832 case svn_depth_files:
5833 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES;
5836 case svn_depth_immediates:
5837 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES;
5840 case svn_depth_infinity:
5841 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY;
5845 /* We don't know how to handle unknown or exclude. */
5846 SVN_ERR_MALFUNCTION();
5850 for (i = 0; i < changelist_filter->nelts; i++)
5853 const char *changelist = APR_ARRAY_IDX(changelist_filter, i,
5856 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5857 STMT_INSERT_TARGET_WITH_CHANGELIST));
5858 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
5859 local_relpath, changelist));
5860 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5862 /* If the root is matched by the changelist, we don't have to match
5863 the children. As that tells us the root is a file */
5864 if (!sub_affected && depth > svn_depth_empty)
5866 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
5867 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
5868 local_relpath, changelist));
5869 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5872 affected_rows += sub_affected;
5875 else /* No changelist filtering */
5882 case svn_depth_empty:
5883 stmt_idx = STMT_INSERT_TARGET;
5886 case svn_depth_files:
5887 stmt_idx = STMT_INSERT_TARGET_DEPTH_FILES;
5890 case svn_depth_immediates:
5891 stmt_idx = STMT_INSERT_TARGET_DEPTH_IMMEDIATES;
5894 case svn_depth_infinity:
5895 stmt_idx = STMT_INSERT_TARGET_DEPTH_INFINITY;
5899 /* We don't know how to handle unknown or exclude. */
5900 SVN_ERR_MALFUNCTION();
5904 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5905 STMT_INSERT_TARGET));
5906 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
5907 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5908 affected_rows += sub_affected;
5910 if (depth > svn_depth_empty)
5912 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
5913 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
5914 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5915 affected_rows += sub_affected;
5919 /* Does the target exist? */
5920 if (affected_rows == 0)
5922 svn_boolean_t exists;
5923 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
5926 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
5927 _("The node '%s' was not found."),
5928 path_for_error_message(wcroot,
5933 return SVN_NO_ERROR;
5938 static svn_error_t *
5939 dump_targets(svn_wc__db_wcroot_t *wcroot,
5940 apr_pool_t *scratch_pool)
5942 svn_sqlite__stmt_t *stmt;
5943 svn_boolean_t have_row;
5945 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5946 STMT_SELECT_TARGETS));
5947 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5950 const char *target = svn_sqlite__column_text(stmt, 0, NULL);
5951 SVN_DBG(("Target: '%s'\n", target));
5952 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5955 SVN_ERR(svn_sqlite__reset(stmt));
5957 return SVN_NO_ERROR;
5962 struct set_changelist_baton_t
5964 const char *new_changelist;
5965 const apr_array_header_t *changelist_filter;
5970 /* The main part of svn_wc__db_op_set_changelist().
5972 * Implements svn_wc__db_txn_callback_t. */
5973 static svn_error_t *
5974 set_changelist_txn(void *baton,
5975 svn_wc__db_wcroot_t *wcroot,
5976 const char *local_relpath,
5977 apr_pool_t *scratch_pool)
5979 struct set_changelist_baton_t *scb = baton;
5980 svn_sqlite__stmt_t *stmt;
5982 SVN_ERR(populate_targets_tree(wcroot, local_relpath, scb->depth,
5983 scb->changelist_filter, scratch_pool));
5985 /* Ensure we have actual nodes for our targets. */
5986 if (scb->new_changelist)
5988 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5989 STMT_INSERT_ACTUAL_EMPTIES));
5990 SVN_ERR(svn_sqlite__step_done(stmt));
5993 /* Now create our notification table. */
5994 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
5995 STMT_CREATE_CHANGELIST_LIST));
5996 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
5997 STMT_CREATE_CHANGELIST_TRIGGER));
5999 /* Update our changelists. */
6000 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6001 STMT_UPDATE_ACTUAL_CHANGELISTS));
6002 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6003 scb->new_changelist));
6004 SVN_ERR(svn_sqlite__step_done(stmt));
6006 if (scb->new_changelist)
6008 /* We have to notify that we skipped directories, so do that now. */
6009 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6010 STMT_MARK_SKIPPED_CHANGELIST_DIRS));
6011 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6012 scb->new_changelist));
6013 SVN_ERR(svn_sqlite__step_done(stmt));
6016 /* We may have left empty ACTUAL nodes, so remove them. This is only a
6017 potential problem if we removed changelists. */
6018 if (!scb->new_changelist)
6020 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6021 STMT_DELETE_ACTUAL_EMPTIES));
6022 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6023 SVN_ERR(svn_sqlite__step_done(stmt));
6026 return SVN_NO_ERROR;
6030 /* Send notifications for svn_wc__db_op_set_changelist().
6032 * Implements work_callback_t. */
6033 static svn_error_t *
6034 do_changelist_notify(void *baton,
6035 svn_wc__db_wcroot_t *wcroot,
6036 svn_cancel_func_t cancel_func,
6038 svn_wc_notify_func2_t notify_func,
6040 apr_pool_t *scratch_pool)
6042 svn_sqlite__stmt_t *stmt;
6043 svn_boolean_t have_row;
6044 apr_pool_t *iterpool;
6046 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6047 STMT_SELECT_CHANGELIST_LIST));
6048 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6050 iterpool = svn_pool_create(scratch_pool);
6053 /* ### wc_id is column 0. use it one day... */
6054 const char *notify_relpath = svn_sqlite__column_text(stmt, 1, NULL);
6055 svn_wc_notify_action_t action = svn_sqlite__column_int(stmt, 2);
6056 svn_wc_notify_t *notify;
6057 const char *notify_abspath;
6059 svn_pool_clear(iterpool);
6063 svn_error_t *err = cancel_func(cancel_baton);
6066 return svn_error_trace(svn_error_compose_create(
6068 svn_sqlite__reset(stmt)));
6071 notify_abspath = svn_dirent_join(wcroot->abspath, notify_relpath,
6073 notify = svn_wc_create_notify(notify_abspath, action, iterpool);
6074 notify->changelist_name = svn_sqlite__column_text(stmt, 3, NULL);
6075 notify_func(notify_baton, notify, iterpool);
6077 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6079 svn_pool_destroy(iterpool);
6081 return svn_error_trace(svn_sqlite__reset(stmt));
6086 svn_wc__db_op_set_changelist(svn_wc__db_t *db,
6087 const char *local_abspath,
6088 const char *new_changelist,
6089 const apr_array_header_t *changelist_filter,
6091 svn_wc_notify_func2_t notify_func,
6093 svn_cancel_func_t cancel_func,
6095 apr_pool_t *scratch_pool)
6097 svn_wc__db_wcroot_t *wcroot;
6098 const char *local_relpath;
6099 struct set_changelist_baton_t scb;
6101 scb.new_changelist = new_changelist;
6102 scb.changelist_filter = changelist_filter;
6105 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6107 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6109 scratch_pool, scratch_pool));
6110 VERIFY_USABLE_WCROOT(wcroot);
6112 /* Flush the entries before we do the work. Even if no work is performed,
6113 the flush isn't a problem. */
6114 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6116 /* Perform the set-changelist operation (transactionally), perform any
6117 notifications necessary, and then clean out our temporary tables. */
6118 return svn_error_trace(with_finalization(wcroot, local_relpath,
6119 set_changelist_txn, &scb,
6120 do_changelist_notify, NULL,
6121 cancel_func, cancel_baton,
6122 notify_func, notify_baton,
6123 STMT_FINALIZE_CHANGELIST,
6127 /* Implementation of svn_wc__db_op_mark_conflict() */
6129 svn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t *wcroot,
6130 const char *local_relpath,
6131 const svn_skel_t *conflict_skel,
6132 apr_pool_t *scratch_pool)
6134 svn_sqlite__stmt_t *stmt;
6135 svn_boolean_t got_row;
6136 svn_boolean_t is_complete;
6138 SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict_skel));
6139 SVN_ERR_ASSERT(is_complete);
6141 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6142 STMT_SELECT_ACTUAL_NODE));
6143 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6144 SVN_ERR(svn_sqlite__step(&got_row, stmt));
6145 SVN_ERR(svn_sqlite__reset(stmt));
6149 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6150 STMT_UPDATE_ACTUAL_CONFLICT));
6151 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6155 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6156 STMT_INSERT_ACTUAL_CONFLICT));
6157 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6158 if (*local_relpath != '\0')
6159 SVN_ERR(svn_sqlite__bind_text(stmt, 4,
6160 svn_relpath_dirname(local_relpath,
6165 svn_stringbuf_t *sb = svn_skel__unparse(conflict_skel, scratch_pool);
6167 SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6170 SVN_ERR(svn_sqlite__update(NULL, stmt));
6172 return SVN_NO_ERROR;
6176 svn_wc__db_op_mark_conflict(svn_wc__db_t *db,
6177 const char *local_abspath,
6178 const svn_skel_t *conflict_skel,
6179 const svn_skel_t *work_items,
6180 apr_pool_t *scratch_pool)
6182 svn_wc__db_wcroot_t *wcroot;
6183 const char *local_relpath;
6185 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6187 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6188 local_abspath, scratch_pool, scratch_pool));
6189 VERIFY_USABLE_WCROOT(wcroot);
6191 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6192 conflict_skel, scratch_pool));
6194 /* ### Should be handled in the same transaction as setting the conflict */
6196 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6198 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6200 return SVN_NO_ERROR;
6204 /* The body of svn_wc__db_op_mark_resolved().
6206 static svn_error_t *
6207 db_op_mark_resolved(svn_wc__db_wcroot_t *wcroot,
6208 const char *local_relpath,
6210 svn_boolean_t resolved_text,
6211 svn_boolean_t resolved_props,
6212 svn_boolean_t resolved_tree,
6213 const svn_skel_t *work_items,
6214 apr_pool_t *scratch_pool)
6216 svn_sqlite__stmt_t *stmt;
6217 svn_boolean_t have_row;
6218 int total_affected_rows = 0;
6219 svn_boolean_t resolved_all;
6220 apr_size_t conflict_len;
6221 const void *conflict_data;
6222 svn_skel_t *conflicts;
6224 /* Check if we have a conflict in ACTUAL */
6225 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6226 STMT_SELECT_ACTUAL_NODE));
6227 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6229 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6233 SVN_ERR(svn_sqlite__reset(stmt));
6235 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6236 STMT_SELECT_NODE_INFO));
6238 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6240 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6241 SVN_ERR(svn_sqlite__reset(stmt));
6244 return SVN_NO_ERROR;
6246 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6247 _("The node '%s' was not found."),
6248 path_for_error_message(wcroot,
6253 conflict_data = svn_sqlite__column_blob(stmt, 2, &conflict_len,
6255 conflicts = svn_skel__parse(conflict_data, conflict_len, scratch_pool);
6256 SVN_ERR(svn_sqlite__reset(stmt));
6258 SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
6259 db, wcroot->abspath,
6261 resolved_props ? "" : NULL,
6263 scratch_pool, scratch_pool));
6265 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6266 STMT_UPDATE_ACTUAL_CONFLICT));
6267 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6271 svn_stringbuf_t *sb = svn_skel__unparse(conflicts, scratch_pool);
6273 SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6276 SVN_ERR(svn_sqlite__update(&total_affected_rows, stmt));
6278 /* Now, remove the actual node if it doesn't have any more useful
6279 information. We only need to do this if we've remove data ourselves. */
6280 if (total_affected_rows > 0)
6282 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6283 STMT_DELETE_ACTUAL_EMPTY));
6284 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6285 SVN_ERR(svn_sqlite__step_done(stmt));
6288 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6290 return SVN_NO_ERROR;
6294 svn_wc__db_op_mark_resolved(svn_wc__db_t *db,
6295 const char *local_abspath,
6296 svn_boolean_t resolved_text,
6297 svn_boolean_t resolved_props,
6298 svn_boolean_t resolved_tree,
6299 const svn_skel_t *work_items,
6300 apr_pool_t *scratch_pool)
6302 svn_wc__db_wcroot_t *wcroot;
6303 const char *local_relpath;
6305 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6307 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6308 local_abspath, scratch_pool, scratch_pool));
6309 VERIFY_USABLE_WCROOT(wcroot);
6311 SVN_WC__DB_WITH_TXN(
6312 db_op_mark_resolved(wcroot, local_relpath, db,
6313 resolved_text, resolved_props, resolved_tree,
6314 work_items, scratch_pool),
6317 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6318 return SVN_NO_ERROR;
6321 /* Clear moved-to information at the delete-half of the move which
6322 * moved LOCAL_RELPATH here. This transforms the move into a simple delete. */
6323 static svn_error_t *
6324 clear_moved_to(const char *local_relpath,
6325 svn_wc__db_wcroot_t *wcroot,
6326 apr_pool_t *scratch_pool)
6328 svn_sqlite__stmt_t *stmt;
6329 svn_boolean_t have_row;
6330 const char *moved_from_relpath;
6332 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6333 STMT_SELECT_MOVED_FROM_RELPATH));
6334 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6335 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6338 SVN_ERR(svn_sqlite__reset(stmt));
6339 return SVN_NO_ERROR;
6342 moved_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
6343 SVN_ERR(svn_sqlite__reset(stmt));
6345 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6346 STMT_CLEAR_MOVED_TO_RELPATH));
6347 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6349 relpath_depth(moved_from_relpath)));
6350 SVN_ERR(svn_sqlite__step_done(stmt));
6352 return SVN_NO_ERROR;
6355 /* One of the two alternative bodies of svn_wc__db_op_revert().
6357 * Implements svn_wc__db_txn_callback_t. */
6358 static svn_error_t *
6359 op_revert_txn(void *baton,
6360 svn_wc__db_wcroot_t *wcroot,
6361 const char *local_relpath,
6362 apr_pool_t *scratch_pool)
6364 svn_wc__db_t *db = baton;
6365 svn_sqlite__stmt_t *stmt;
6366 svn_boolean_t have_row;
6368 svn_boolean_t moved_here;
6370 const char *moved_to;
6372 /* ### Similar structure to op_revert_recursive_txn, should they be
6375 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6376 STMT_SELECT_NODE_INFO));
6377 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6378 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6381 SVN_ERR(svn_sqlite__reset(stmt));
6383 /* There was no NODE row, so attempt to delete an ACTUAL_NODE row. */
6384 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6385 STMT_DELETE_ACTUAL_NODE));
6386 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6387 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6390 /* Can't do non-recursive actual-only revert if actual-only
6391 children exist. Raise an error to cancel the transaction. */
6392 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6393 STMT_ACTUAL_HAS_CHILDREN));
6394 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6395 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6396 SVN_ERR(svn_sqlite__reset(stmt));
6398 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6399 _("Can't revert '%s' without"
6400 " reverting children"),
6401 path_for_error_message(wcroot,
6404 return SVN_NO_ERROR;
6407 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6408 _("The node '%s' was not found."),
6409 path_for_error_message(wcroot,
6414 op_depth = svn_sqlite__column_int(stmt, 0);
6415 moved_here = svn_sqlite__column_boolean(stmt, 15);
6416 moved_to = svn_sqlite__column_text(stmt, 17, scratch_pool);
6417 SVN_ERR(svn_sqlite__reset(stmt));
6421 SVN_ERR(svn_wc__db_resolve_break_moved_away_internal(wcroot,
6428 svn_skel_t *conflict;
6430 SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, wcroot,
6432 scratch_pool, scratch_pool));
6435 svn_wc_operation_t operation;
6436 svn_boolean_t tree_conflicted;
6438 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
6440 db, wcroot->abspath,
6442 scratch_pool, scratch_pool));
6444 && (operation == svn_wc_operation_update
6445 || operation == svn_wc_operation_switch))
6447 svn_wc_conflict_reason_t reason;
6448 svn_wc_conflict_action_t action;
6450 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
6452 db, wcroot->abspath,
6457 if (reason == svn_wc_conflict_reason_deleted)
6458 SVN_ERR(svn_wc__db_resolve_delete_raise_moved_away(
6459 db, svn_dirent_join(wcroot->abspath, local_relpath,
6461 NULL, NULL /* ### How do we notify this? */,
6467 if (op_depth > 0 && op_depth == relpath_depth(local_relpath))
6469 /* Can't do non-recursive revert if children exist */
6470 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6471 STMT_SELECT_GE_OP_DEPTH_CHILDREN));
6472 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6473 local_relpath, op_depth));
6474 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6475 SVN_ERR(svn_sqlite__reset(stmt));
6477 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6478 _("Can't revert '%s' without"
6479 " reverting children"),
6480 path_for_error_message(wcroot,
6484 /* Rewrite the op-depth of all deleted children making the
6485 direct children into roots of deletes. */
6486 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6487 STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE));
6488 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6491 SVN_ERR(svn_sqlite__step_done(stmt));
6493 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6494 STMT_DELETE_WORKING_NODE));
6495 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6496 SVN_ERR(svn_sqlite__step_done(stmt));
6498 /* ### This removes the lock, but what about the access baton? */
6499 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6500 STMT_DELETE_WC_LOCK_ORPHAN));
6501 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6502 SVN_ERR(svn_sqlite__step_done(stmt));
6504 /* If this node was moved-here, clear moved-to at the move source. */
6506 SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool));
6509 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6510 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST));
6511 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6512 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6515 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6516 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST));
6517 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6518 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6521 return SVN_NO_ERROR;
6525 /* One of the two alternative bodies of svn_wc__db_op_revert().
6527 * Implements svn_wc__db_txn_callback_t. */
6528 static svn_error_t *
6529 op_revert_recursive_txn(void *baton,
6530 svn_wc__db_wcroot_t *wcroot,
6531 const char *local_relpath,
6532 apr_pool_t *scratch_pool)
6534 svn_sqlite__stmt_t *stmt;
6535 svn_boolean_t have_row;
6537 int select_op_depth;
6538 svn_boolean_t moved_here;
6540 apr_pool_t *iterpool;
6542 /* ### Similar structure to op_revert_txn, should they be
6545 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6546 STMT_SELECT_NODE_INFO));
6547 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6548 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6551 SVN_ERR(svn_sqlite__reset(stmt));
6553 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6554 STMT_DELETE_ACTUAL_NODE));
6555 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
6557 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6560 return SVN_NO_ERROR; /* actual-only revert */
6562 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6563 _("The node '%s' was not found."),
6564 path_for_error_message(wcroot,
6569 op_depth = svn_sqlite__column_int(stmt, 0);
6570 moved_here = svn_sqlite__column_boolean(stmt, 15);
6571 SVN_ERR(svn_sqlite__reset(stmt));
6573 if (op_depth > 0 && op_depth != relpath_depth(local_relpath))
6574 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6575 _("Can't revert '%s' without"
6576 " reverting parent"),
6577 path_for_error_message(wcroot,
6581 /* Remove moved-here from move destinations outside the tree. */
6582 SVN_ERR(svn_sqlite__get_statement(
6583 &stmt, wcroot->sdb, STMT_SELECT_MOVED_OUTSIDE));
6584 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
6586 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6589 const char *move_src_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6590 int move_op_depth = svn_sqlite__column_int(stmt, 2);
6593 err = svn_wc__db_resolve_break_moved_away_internal(wcroot,
6598 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
6600 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6602 SVN_ERR(svn_sqlite__reset(stmt));
6604 /* Don't delete BASE nodes */
6605 select_op_depth = op_depth ? op_depth : 1;
6607 /* Reverting any non wc-root node */
6608 SVN_ERR(svn_sqlite__get_statement(
6610 STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
6611 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6612 local_relpath, select_op_depth));
6613 SVN_ERR(svn_sqlite__step_done(stmt));
6615 SVN_ERR(svn_sqlite__get_statement(
6617 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
6618 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6619 SVN_ERR(svn_sqlite__step_done(stmt));
6621 SVN_ERR(svn_sqlite__get_statement(
6623 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
6624 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6625 SVN_ERR(svn_sqlite__step_done(stmt));
6627 /* ### This removes the locks, but what about the access batons? */
6628 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6629 STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
6630 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
6632 SVN_ERR(svn_sqlite__step_done(stmt));
6634 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6635 STMT_SELECT_MOVED_HERE_CHILDREN));
6636 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6638 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6640 iterpool = svn_pool_create(scratch_pool);
6643 const char *moved_here_child_relpath;
6646 svn_pool_clear(iterpool);
6648 moved_here_child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
6649 err = clear_moved_to(moved_here_child_relpath, wcroot, iterpool);
6651 return svn_error_trace(svn_error_compose_create(
6653 svn_sqlite__reset(stmt)));
6655 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6657 SVN_ERR(svn_sqlite__reset(stmt));
6658 svn_pool_destroy(iterpool);
6660 /* Clear potential moved-to pointing at the target node itself. */
6661 if (op_depth > 0 && op_depth == relpath_depth(local_relpath)
6663 SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool));
6665 return SVN_NO_ERROR;
6669 svn_wc__db_op_revert(svn_wc__db_t *db,
6670 const char *local_abspath,
6672 apr_pool_t *result_pool,
6673 apr_pool_t *scratch_pool)
6675 svn_wc__db_wcroot_t *wcroot;
6676 const char *local_relpath;
6677 struct with_triggers_baton_t wtb = { STMT_CREATE_REVERT_LIST,
6678 STMT_DROP_REVERT_LIST_TRIGGERS,
6681 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6685 case svn_depth_empty:
6686 wtb.cb_func = op_revert_txn;
6689 case svn_depth_infinity:
6690 wtb.cb_func = op_revert_recursive_txn;
6693 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
6694 _("Unsupported depth for revert of '%s'"),
6695 svn_dirent_local_style(local_abspath,
6699 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6700 db, local_abspath, scratch_pool, scratch_pool));
6701 VERIFY_USABLE_WCROOT(wcroot);
6703 SVN_WC__DB_WITH_TXN(with_triggers(&wtb, wcroot, local_relpath, scratch_pool),
6706 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6708 return SVN_NO_ERROR;
6711 /* The body of svn_wc__db_revert_list_read().
6713 static svn_error_t *
6714 revert_list_read(svn_boolean_t *reverted,
6715 const apr_array_header_t **marker_paths,
6716 svn_boolean_t *copied_here,
6717 svn_node_kind_t *kind,
6718 svn_wc__db_wcroot_t *wcroot,
6719 const char *local_relpath,
6721 apr_pool_t *result_pool,
6722 apr_pool_t *scratch_pool)
6724 svn_sqlite__stmt_t *stmt;
6725 svn_boolean_t have_row;
6728 *marker_paths = NULL;
6729 *copied_here = FALSE;
6730 *kind = svn_node_unknown;
6732 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6733 STMT_SELECT_REVERT_LIST));
6734 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6735 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6738 svn_boolean_t is_actual = svn_sqlite__column_boolean(stmt, 0);
6739 svn_boolean_t another_row = FALSE;
6743 apr_size_t conflict_len;
6744 const void *conflict_data;
6746 conflict_data = svn_sqlite__column_blob(stmt, 5, &conflict_len,
6750 svn_skel_t *conflicts = svn_skel__parse(conflict_data,
6754 SVN_ERR(svn_wc__conflict_read_markers(marker_paths,
6755 db, wcroot->abspath,
6761 if (!svn_sqlite__column_is_null(stmt, 1)) /* notify */
6764 SVN_ERR(svn_sqlite__step(&another_row, stmt));
6767 if (!is_actual || another_row)
6770 if (!svn_sqlite__column_is_null(stmt, 4)) /* repos_id */
6772 int op_depth = svn_sqlite__column_int(stmt, 3);
6773 *copied_here = (op_depth == relpath_depth(local_relpath));
6775 *kind = svn_sqlite__column_token(stmt, 2, kind_map);
6779 SVN_ERR(svn_sqlite__reset(stmt));
6783 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6784 STMT_DELETE_REVERT_LIST));
6785 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6786 SVN_ERR(svn_sqlite__step_done(stmt));
6789 return SVN_NO_ERROR;
6793 svn_wc__db_revert_list_read(svn_boolean_t *reverted,
6794 const apr_array_header_t **marker_files,
6795 svn_boolean_t *copied_here,
6796 svn_node_kind_t *kind,
6798 const char *local_abspath,
6799 apr_pool_t *result_pool,
6800 apr_pool_t *scratch_pool)
6802 svn_wc__db_wcroot_t *wcroot;
6803 const char *local_relpath;
6805 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6806 db, local_abspath, scratch_pool, scratch_pool));
6807 VERIFY_USABLE_WCROOT(wcroot);
6809 SVN_WC__DB_WITH_TXN(
6810 revert_list_read(reverted, marker_files, copied_here, kind,
6811 wcroot, local_relpath, db,
6812 result_pool, scratch_pool),
6814 return SVN_NO_ERROR;
6818 /* The body of svn_wc__db_revert_list_read_copied_children().
6820 static svn_error_t *
6821 revert_list_read_copied_children(svn_wc__db_wcroot_t *wcroot,
6822 const char *local_relpath,
6823 const apr_array_header_t **children_p,
6824 apr_pool_t *result_pool,
6825 apr_pool_t *scratch_pool)
6827 svn_sqlite__stmt_t *stmt;
6828 svn_boolean_t have_row;
6829 apr_array_header_t *children;
6832 apr_array_make(result_pool, 0,
6833 sizeof(svn_wc__db_revert_list_copied_child_info_t *));
6835 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6836 STMT_SELECT_REVERT_LIST_COPIED_CHILDREN));
6837 SVN_ERR(svn_sqlite__bindf(stmt, "sd",
6838 local_relpath, relpath_depth(local_relpath)));
6839 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6842 svn_wc__db_revert_list_copied_child_info_t *child_info;
6843 const char *child_relpath;
6845 child_info = apr_palloc(result_pool, sizeof(*child_info));
6847 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6848 child_info->abspath = svn_dirent_join(wcroot->abspath, child_relpath,
6850 child_info->kind = svn_sqlite__column_token(stmt, 1, kind_map);
6853 svn_wc__db_revert_list_copied_child_info_t *) = child_info;
6855 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6857 SVN_ERR(svn_sqlite__reset(stmt));
6859 *children_p = children;
6861 return SVN_NO_ERROR;
6866 svn_wc__db_revert_list_read_copied_children(const apr_array_header_t **children,
6868 const char *local_abspath,
6869 apr_pool_t *result_pool,
6870 apr_pool_t *scratch_pool)
6872 svn_wc__db_wcroot_t *wcroot;
6873 const char *local_relpath;
6875 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6876 db, local_abspath, scratch_pool, scratch_pool));
6877 VERIFY_USABLE_WCROOT(wcroot);
6879 SVN_WC__DB_WITH_TXN(
6880 revert_list_read_copied_children(wcroot, local_relpath, children,
6881 result_pool, scratch_pool),
6883 return SVN_NO_ERROR;
6888 svn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func,
6891 const char *local_abspath,
6892 apr_pool_t *scratch_pool)
6894 svn_wc__db_wcroot_t *wcroot;
6895 const char *local_relpath;
6896 svn_sqlite__stmt_t *stmt;
6897 svn_boolean_t have_row;
6898 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
6900 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6901 db, local_abspath, scratch_pool, iterpool));
6902 VERIFY_USABLE_WCROOT(wcroot);
6904 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6905 STMT_SELECT_REVERT_LIST_RECURSIVE));
6906 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6907 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6909 return svn_error_trace(svn_sqlite__reset(stmt)); /* optimise for no row */
6912 const char *notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6914 svn_pool_clear(iterpool);
6916 notify_func(notify_baton,
6917 svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
6920 svn_wc_notify_revert,
6924 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6926 SVN_ERR(svn_sqlite__reset(stmt));
6928 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6929 STMT_DELETE_REVERT_LIST_RECURSIVE));
6930 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6931 SVN_ERR(svn_sqlite__step_done(stmt));
6933 svn_pool_destroy(iterpool);
6935 return SVN_NO_ERROR;
6939 svn_wc__db_revert_list_done(svn_wc__db_t *db,
6940 const char *local_abspath,
6941 apr_pool_t *scratch_pool)
6943 svn_wc__db_wcroot_t *wcroot;
6944 const char *local_relpath;
6946 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6947 db, local_abspath, scratch_pool, scratch_pool));
6948 VERIFY_USABLE_WCROOT(wcroot);
6950 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_DROP_REVERT_LIST));
6952 return SVN_NO_ERROR;
6955 /* The body of svn_wc__db_op_remove_node().
6957 static svn_error_t *
6958 remove_node_txn(svn_boolean_t *left_changes,
6959 svn_wc__db_wcroot_t *wcroot,
6960 const char *local_relpath,
6962 svn_boolean_t destroy_wc,
6963 svn_boolean_t destroy_changes,
6964 svn_revnum_t not_present_rev,
6965 svn_wc__db_status_t not_present_status,
6966 svn_node_kind_t not_present_kind,
6967 const svn_skel_t *conflict,
6968 const svn_skel_t *work_items,
6969 svn_cancel_func_t cancel_func,
6971 apr_pool_t *scratch_pool)
6973 svn_sqlite__stmt_t *stmt;
6975 apr_int64_t repos_id;
6976 const char *repos_relpath;
6978 /* Note that unlike many similar functions it is a valid scenario for this
6979 function to be called on a wcroot! */
6981 /* db set when destroying wc */
6982 SVN_ERR_ASSERT(!destroy_wc || db != NULL);
6985 *left_changes = FALSE;
6987 /* Need info for not_present node? */
6988 if (SVN_IS_VALID_REVNUM(not_present_rev))
6989 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
6990 &repos_relpath, &repos_id,
6991 NULL, NULL, NULL, NULL, NULL,
6992 NULL, NULL, NULL, NULL, NULL,
6993 wcroot, local_relpath,
6994 scratch_pool, scratch_pool));
6997 && (!destroy_changes || *local_relpath == '\0'))
6999 svn_boolean_t have_row;
7000 apr_pool_t *iterpool;
7001 svn_error_t *err = NULL;
7003 /* Install WQ items for deleting the unmodified files and all dirs */
7004 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7005 STMT_SELECT_WORKING_PRESENT));
7006 SVN_ERR(svn_sqlite__bindf(stmt, "is",
7007 wcroot->wc_id, local_relpath));
7009 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7011 iterpool = svn_pool_create(scratch_pool);
7015 const char *child_relpath;
7016 const char *child_abspath;
7017 svn_node_kind_t child_kind;
7018 svn_boolean_t have_checksum;
7019 svn_filesize_t recorded_size;
7020 apr_int64_t recorded_time;
7021 const svn_io_dirent2_t *dirent;
7022 svn_boolean_t modified_p = TRUE;
7023 svn_skel_t *work_item = NULL;
7025 svn_pool_clear(iterpool);
7027 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7028 child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
7030 child_abspath = svn_dirent_join(wcroot->abspath, child_relpath,
7033 if (child_kind == svn_node_file)
7035 have_checksum = !svn_sqlite__column_is_null(stmt, 2);
7036 recorded_size = get_recorded_size(stmt, 3);
7037 recorded_time = svn_sqlite__column_int64(stmt, 4);
7041 err = cancel_func(cancel_baton);
7046 err = svn_io_stat_dirent2(&dirent, child_abspath, FALSE, TRUE,
7047 iterpool, iterpool);
7053 || dirent->kind != svn_node_file
7054 || child_kind != svn_node_file)
7056 /* Not interested in keeping changes */
7059 else if (child_kind == svn_node_file
7060 && dirent->kind == svn_node_file
7061 && dirent->filesize == recorded_size
7062 && dirent->mtime == recorded_time)
7064 modified_p = FALSE; /* File matches recorded state */
7066 else if (have_checksum)
7067 err = svn_wc__internal_file_modified_p(&modified_p,
7077 *left_changes = TRUE;
7079 else if (child_kind == svn_node_dir)
7081 err = svn_wc__wq_build_dir_remove(&work_item,
7082 db, wcroot->abspath,
7083 child_abspath, FALSE,
7084 iterpool, iterpool);
7086 else /* svn_node_file || svn_node_symlink */
7088 err = svn_wc__wq_build_file_remove(&work_item,
7089 db, wcroot->abspath,
7091 iterpool, iterpool);
7099 err = add_work_items(wcroot->sdb, work_item, iterpool);
7104 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7106 svn_pool_destroy(iterpool);
7108 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
7111 if (destroy_wc && *local_relpath != '\0')
7113 /* Create work item for destroying the root */
7114 svn_wc__db_status_t status;
7115 svn_node_kind_t kind;
7116 SVN_ERR(read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, NULL,
7117 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7118 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7119 wcroot, local_relpath,
7120 scratch_pool, scratch_pool));
7122 if (status == svn_wc__db_status_normal
7123 || status == svn_wc__db_status_added
7124 || status == svn_wc__db_status_incomplete)
7126 svn_skel_t *work_item = NULL;
7127 const char *local_abspath = svn_dirent_join(wcroot->abspath,
7131 if (kind == svn_node_dir)
7133 SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
7134 db, wcroot->abspath,
7138 scratch_pool, scratch_pool));
7142 svn_boolean_t modified_p = FALSE;
7144 if (!destroy_changes)
7146 SVN_ERR(svn_wc__internal_file_modified_p(&modified_p,
7153 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
7154 db, wcroot->abspath,
7161 *left_changes = TRUE;
7165 SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
7169 /* Remove all nodes below local_relpath */
7170 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7171 STMT_DELETE_NODE_RECURSIVE));
7172 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7173 SVN_ERR(svn_sqlite__step_done(stmt));
7175 /* Delete the root NODE when this is not the working copy root */
7176 if (local_relpath[0] != '\0')
7178 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7180 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7181 SVN_ERR(svn_sqlite__step_done(stmt));
7184 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7185 STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7187 /* Delete all actual nodes at or below local_relpath */
7188 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7190 SVN_ERR(svn_sqlite__step_done(stmt));
7192 /* Should we leave a not-present node? */
7193 if (SVN_IS_VALID_REVNUM(not_present_rev))
7195 insert_base_baton_t ibb;
7198 ibb.repos_id = repos_id;
7200 SVN_ERR_ASSERT(not_present_status == svn_wc__db_status_not_present
7201 || not_present_status == svn_wc__db_status_excluded);
7203 ibb.status = not_present_status;
7204 ibb.kind = not_present_kind;
7206 ibb.repos_relpath = repos_relpath;
7207 ibb.revision = not_present_rev;
7209 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
7212 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
7214 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
7215 conflict, scratch_pool));
7217 return SVN_NO_ERROR;
7221 svn_wc__db_op_remove_node(svn_boolean_t *left_changes,
7223 const char *local_abspath,
7224 svn_boolean_t destroy_wc,
7225 svn_boolean_t destroy_changes,
7226 svn_revnum_t not_present_revision,
7227 svn_wc__db_status_t not_present_status,
7228 svn_node_kind_t not_present_kind,
7229 const svn_skel_t *conflict,
7230 const svn_skel_t *work_items,
7231 svn_cancel_func_t cancel_func,
7233 apr_pool_t *scratch_pool)
7235 svn_wc__db_wcroot_t *wcroot;
7236 const char *local_relpath;
7238 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7240 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7241 local_abspath, scratch_pool, scratch_pool));
7242 VERIFY_USABLE_WCROOT(wcroot);
7244 SVN_WC__DB_WITH_TXN(remove_node_txn(left_changes,
7245 wcroot, local_relpath, db,
7246 destroy_wc, destroy_changes,
7247 not_present_revision, not_present_status,
7248 not_present_kind, conflict, work_items,
7249 cancel_func, cancel_baton, scratch_pool),
7252 /* Flush everything below this node in all ways */
7253 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
7256 return SVN_NO_ERROR;
7260 /* The body of svn_wc__db_op_set_base_depth().
7262 static svn_error_t *
7263 db_op_set_base_depth(svn_wc__db_wcroot_t *wcroot,
7264 const char *local_relpath,
7266 apr_pool_t *scratch_pool)
7268 svn_sqlite__stmt_t *stmt;
7271 /* Flush any entries before we start monkeying the database. */
7272 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7273 STMT_UPDATE_NODE_BASE_DEPTH));
7274 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
7275 svn_token__to_word(depth_map, depth)));
7276 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7278 if (affected_rows == 0)
7279 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7280 "The node '%s' is not a committed directory",
7281 path_for_error_message(wcroot, local_relpath,
7284 return SVN_NO_ERROR;
7289 svn_wc__db_op_set_base_depth(svn_wc__db_t *db,
7290 const char *local_abspath,
7292 apr_pool_t *scratch_pool)
7294 svn_wc__db_wcroot_t *wcroot;
7295 const char *local_relpath;
7297 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7298 SVN_ERR_ASSERT(depth >= svn_depth_empty && depth <= svn_depth_infinity);
7300 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7301 local_abspath, scratch_pool, scratch_pool));
7302 VERIFY_USABLE_WCROOT(wcroot);
7304 /* ### We set depth on working and base to match entry behavior.
7305 Maybe these should be separated later? */
7306 SVN_WC__DB_WITH_TXN(db_op_set_base_depth(wcroot, local_relpath, depth,
7310 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
7312 return SVN_NO_ERROR;
7316 static svn_error_t *
7317 info_below_working(svn_boolean_t *have_base,
7318 svn_boolean_t *have_work,
7319 svn_wc__db_status_t *status,
7320 svn_wc__db_wcroot_t *wcroot,
7321 const char *local_relpath,
7322 int below_op_depth, /* < 0 is ignored */
7323 apr_pool_t *scratch_pool);
7326 /* Convert STATUS, the raw status obtained from the presence map, to
7327 the status appropriate for a working (op_depth > 0) node and return
7328 it in *WORKING_STATUS. */
7329 static svn_error_t *
7330 convert_to_working_status(svn_wc__db_status_t *working_status,
7331 svn_wc__db_status_t status)
7333 svn_wc__db_status_t work_status = status;
7335 SVN_ERR_ASSERT(work_status == svn_wc__db_status_normal
7336 || work_status == svn_wc__db_status_not_present
7337 || work_status == svn_wc__db_status_base_deleted
7338 || work_status == svn_wc__db_status_incomplete
7339 || work_status == svn_wc__db_status_excluded);
7341 if (work_status == svn_wc__db_status_excluded)
7343 *working_status = svn_wc__db_status_excluded;
7345 else if (work_status == svn_wc__db_status_not_present
7346 || work_status == svn_wc__db_status_base_deleted)
7348 /* The caller should scan upwards to detect whether this
7349 deletion has occurred because this node has been moved
7350 away, or it is a regular deletion. Also note that the
7351 deletion could be of the BASE tree, or a child of
7352 something that has been copied/moved here. */
7354 *working_status = svn_wc__db_status_deleted;
7356 else /* normal or incomplete */
7358 /* The caller should scan upwards to detect whether this
7359 addition has occurred because of a simple addition,
7360 a copy, or is the destination of a move. */
7361 *working_status = svn_wc__db_status_added;
7364 return SVN_NO_ERROR;
7368 /* Return the status of the node, if any, below the "working" node (or
7369 below BELOW_OP_DEPTH if >= 0).
7370 Set *HAVE_BASE or *HAVE_WORK to indicate if a base node or lower
7371 working node is present, and *STATUS to the status of the first
7372 layer below the selected node. */
7373 static svn_error_t *
7374 info_below_working(svn_boolean_t *have_base,
7375 svn_boolean_t *have_work,
7376 svn_wc__db_status_t *status,
7377 svn_wc__db_wcroot_t *wcroot,
7378 const char *local_relpath,
7380 apr_pool_t *scratch_pool)
7382 svn_sqlite__stmt_t *stmt;
7383 svn_boolean_t have_row;
7385 *have_base = *have_work = FALSE;
7386 *status = svn_wc__db_status_normal;
7388 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7389 STMT_SELECT_NODE_INFO));
7390 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7391 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7393 if (below_op_depth >= 0)
7396 (svn_sqlite__column_int(stmt, 0) > below_op_depth))
7398 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7403 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7405 *status = svn_sqlite__column_token(stmt, 3, presence_map);
7409 int op_depth = svn_sqlite__column_int(stmt, 0);
7416 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7419 SVN_ERR(svn_sqlite__reset(stmt));
7422 SVN_ERR(convert_to_working_status(status, *status));
7424 return SVN_NO_ERROR;
7427 /* Helper function for op_delete_txn */
7428 static svn_error_t *
7429 delete_update_movedto(svn_wc__db_wcroot_t *wcroot,
7430 const char *child_moved_from_relpath,
7432 const char *new_moved_to_relpath,
7433 apr_pool_t *scratch_pool)
7435 svn_sqlite__stmt_t *stmt;
7438 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7439 STMT_UPDATE_MOVED_TO_RELPATH));
7441 SVN_ERR(svn_sqlite__bindf(stmt, "isds",
7443 child_moved_from_relpath,
7445 new_moved_to_relpath));
7446 SVN_ERR(svn_sqlite__update(&affected, stmt));
7447 assert(affected == 1);
7449 return SVN_NO_ERROR;
7453 struct op_delete_baton_t {
7454 const char *moved_to_relpath; /* NULL if delete is not part of a move */
7455 svn_skel_t *conflict;
7456 svn_skel_t *work_items;
7457 svn_boolean_t delete_dir_externals;
7458 svn_boolean_t notify;
7461 /* This structure is used while rewriting move information for nodes.
7463 * The most simple case of rewriting move information happens when
7464 * a moved-away subtree is moved again: mv A B; mv B C
7465 * The second move requires rewriting moved-to info at or within A.
7467 * Another example is a move of a subtree which had nodes moved into it:
7469 * This requires rewriting such that A/F is marked has having moved to G/F.
7471 * Another case is where a node becomes a nested moved node.
7472 * A nested move happens when a subtree child is moved before or after
7473 * the subtree itself is moved. For example:
7474 * mv A/F A/G; mv A B
7475 * In this case, the move A/F -> A/G is rewritten to B/F -> B/G.
7476 * Note that the following sequence results in the same DB state:
7477 * mv A B; mv B/F B/G
7478 * We do not care about the order the moves were performed in.
7479 * For details, see http://wiki.apache.org/subversion/MultiLayerMoves
7481 struct moved_node_t {
7482 /* The source of the move. */
7483 const char *local_relpath;
7485 /* The move destination. */
7486 const char *moved_to_relpath;
7488 /* The op-depth of the deleted node at the source of the move. */
7492 static svn_error_t *
7493 delete_node(void *baton,
7494 svn_wc__db_wcroot_t *wcroot,
7495 const char *local_relpath,
7496 apr_pool_t *scratch_pool)
7498 struct op_delete_baton_t *b = baton;
7499 svn_wc__db_status_t status;
7500 svn_boolean_t have_row, op_root;
7501 svn_boolean_t add_work = FALSE;
7502 svn_sqlite__stmt_t *stmt;
7503 int select_depth; /* Depth of what is to be deleted */
7504 svn_boolean_t refetch_depth = FALSE;
7505 svn_node_kind_t kind;
7506 apr_array_header_t *moved_nodes = NULL;
7507 int delete_depth = relpath_depth(local_relpath);
7509 SVN_ERR(read_info(&status,
7510 &kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7511 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7512 &op_root, NULL, NULL,
7514 wcroot, local_relpath,
7515 scratch_pool, scratch_pool));
7517 if (status == svn_wc__db_status_deleted
7518 || status == svn_wc__db_status_not_present)
7519 return SVN_NO_ERROR;
7521 /* Don't copy BASE directories with server excluded nodes */
7522 if (status == svn_wc__db_status_normal && kind == svn_node_dir)
7524 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7525 STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
7526 SVN_ERR(svn_sqlite__bindf(stmt, "is",
7527 wcroot->wc_id, local_relpath));
7528 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7531 const char *absent_path = svn_sqlite__column_text(stmt, 0,
7534 return svn_error_createf(
7535 SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
7536 svn_sqlite__reset(stmt),
7537 _("Cannot delete '%s' as '%s' is excluded by server"),
7538 path_for_error_message(wcroot, local_relpath,
7540 path_for_error_message(wcroot, absent_path,
7543 SVN_ERR(svn_sqlite__reset(stmt));
7545 else if (status == svn_wc__db_status_server_excluded)
7547 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
7548 _("Cannot delete '%s' as it is excluded by server"),
7549 path_for_error_message(wcroot, local_relpath,
7552 else if (status == svn_wc__db_status_excluded)
7554 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
7555 _("Cannot delete '%s' as it is excluded"),
7556 path_for_error_message(wcroot, local_relpath,
7560 if (b->moved_to_relpath)
7562 const char *moved_from_relpath = NULL;
7563 struct moved_node_t *moved_node;
7566 moved_nodes = apr_array_make(scratch_pool, 1,
7567 sizeof(struct moved_node_t *));
7569 /* The node is being moved-away.
7570 * Figure out if the node was moved-here before, or whether this
7571 * is the first time the node is moved. */
7572 if (status == svn_wc__db_status_added)
7573 SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL,
7574 &moved_from_relpath,
7577 wcroot, local_relpath,
7578 scratch_pool, scratch_pool));
7580 if (op_root && moved_from_relpath)
7582 const char *part = svn_relpath_skip_ancestor(local_relpath,
7583 moved_from_relpath);
7585 /* Existing move-root is moved to another location */
7586 moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
7588 moved_node->local_relpath = moved_from_relpath;
7590 moved_node->local_relpath = svn_relpath_join(b->moved_to_relpath,
7591 part, scratch_pool);
7592 moved_node->op_depth = move_op_depth;
7593 moved_node->moved_to_relpath = b->moved_to_relpath;
7595 APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
7597 else if (!op_root && (status == svn_wc__db_status_normal
7598 || status == svn_wc__db_status_copied
7599 || status == svn_wc__db_status_moved_here))
7601 /* The node is becoming a move-root for the first time,
7602 * possibly because of a nested move operation. */
7603 moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
7604 moved_node->local_relpath = local_relpath;
7605 moved_node->op_depth = delete_depth;
7606 moved_node->moved_to_relpath = b->moved_to_relpath;
7608 APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
7610 /* Else: We can't track history of local additions and/or of things we are
7613 /* And update all moved_to values still pointing to this location */
7614 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7615 STMT_UPDATE_MOVED_TO_DESCENDANTS));
7616 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
7618 b->moved_to_relpath));
7619 SVN_ERR(svn_sqlite__update(NULL, stmt));
7623 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7624 STMT_CLEAR_MOVED_TO_DESCENDANTS));
7625 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7627 SVN_ERR(svn_sqlite__update(NULL, stmt));
7630 /* Find children that were moved out of the subtree rooted at this node.
7631 * We'll need to update their op-depth columns because their deletion
7632 * is now implied by the deletion of their parent (i.e. this node). */
7634 apr_pool_t *iterpool;
7636 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7637 STMT_SELECT_MOVED_FOR_DELETE));
7638 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7640 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7641 iterpool = svn_pool_create(scratch_pool);
7644 struct moved_node_t *mn;
7645 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7646 const char *mv_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
7647 int child_op_depth = svn_sqlite__column_int(stmt, 2);
7648 svn_boolean_t fixup = FALSE;
7650 if (!b->moved_to_relpath
7651 && ! svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
7653 /* Update the op-depth of an moved node below this tree */
7655 child_op_depth = delete_depth;
7657 else if (b->moved_to_relpath
7658 && delete_depth == child_op_depth)
7660 /* Update the op-depth of a tree shadowed by this tree */
7662 child_op_depth = delete_depth;
7664 else if (b->moved_to_relpath
7665 && child_op_depth >= delete_depth
7666 && !svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
7668 /* Update the move destination of something that is now moved
7671 child_relpath = svn_relpath_skip_ancestor(local_relpath, child_relpath);
7675 child_relpath = svn_relpath_join(b->moved_to_relpath, child_relpath, scratch_pool);
7677 if (child_op_depth > delete_depth
7678 && svn_relpath_skip_ancestor(local_relpath, child_relpath))
7679 child_op_depth = delete_depth;
7681 child_op_depth = relpath_depth(child_relpath);
7689 mn = apr_pcalloc(scratch_pool, sizeof(struct moved_node_t));
7691 mn->local_relpath = apr_pstrdup(scratch_pool, child_relpath);
7692 mn->moved_to_relpath = apr_pstrdup(scratch_pool, mv_to_relpath);
7693 mn->op_depth = child_op_depth;
7696 moved_nodes = apr_array_make(scratch_pool, 1,
7697 sizeof(struct moved_node_t *));
7698 APR_ARRAY_PUSH(moved_nodes, struct moved_node_t *) = mn;
7701 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7703 svn_pool_destroy(iterpool);
7704 SVN_ERR(svn_sqlite__reset(stmt));
7709 svn_boolean_t below_base;
7710 svn_boolean_t below_work;
7711 svn_wc__db_status_t below_status;
7713 /* Use STMT_SELECT_NODE_INFO directly instead of read_info plus
7714 info_below_working */
7715 SVN_ERR(info_below_working(&below_base, &below_work, &below_status,
7716 wcroot, local_relpath, -1, scratch_pool));
7717 if ((below_base || below_work)
7718 && below_status != svn_wc__db_status_not_present
7719 && below_status != svn_wc__db_status_deleted)
7722 refetch_depth = TRUE;
7725 select_depth = relpath_depth(local_relpath);
7730 if (status != svn_wc__db_status_normal)
7731 SVN_ERR(op_depth_of(&select_depth, wcroot, local_relpath));
7733 select_depth = 0; /* Deleting BASE node */
7736 /* ### Put actual-only nodes into the list? */
7739 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7740 STMT_INSERT_DELETE_LIST));
7741 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
7742 wcroot->wc_id, local_relpath, select_depth));
7743 SVN_ERR(svn_sqlite__step_done(stmt));
7746 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7747 STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
7748 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
7749 wcroot->wc_id, local_relpath, delete_depth));
7750 SVN_ERR(svn_sqlite__step_done(stmt));
7753 SVN_ERR(op_depth_of(&select_depth, wcroot, local_relpath));
7755 /* Delete ACTUAL_NODE rows, but leave those that have changelist
7757 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7758 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
7759 SVN_ERR(svn_sqlite__bindf(stmt, "is",
7760 wcroot->wc_id, local_relpath));
7761 SVN_ERR(svn_sqlite__step_done(stmt));
7763 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7764 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
7765 SVN_ERR(svn_sqlite__bindf(stmt, "is",
7766 wcroot->wc_id, local_relpath));
7767 SVN_ERR(svn_sqlite__step_done(stmt));
7769 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7770 STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
7771 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7773 SVN_ERR(svn_sqlite__step_done(stmt));
7777 /* Delete the node at LOCAL_RELPATH, and possibly mark it as moved. */
7779 /* Delete the node and possible descendants. */
7780 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7781 STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
7782 SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
7783 wcroot->wc_id, local_relpath,
7784 select_depth, delete_depth));
7785 SVN_ERR(svn_sqlite__step_done(stmt));
7792 for (i = 0; i < moved_nodes->nelts; ++i)
7794 const struct moved_node_t *moved_node
7795 = APR_ARRAY_IDX(moved_nodes, i, void *);
7797 SVN_ERR(delete_update_movedto(wcroot,
7798 moved_node->local_relpath,
7799 moved_node->op_depth,
7800 moved_node->moved_to_relpath,
7805 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7806 STMT_DELETE_FILE_EXTERNALS));
7807 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7808 SVN_ERR(svn_sqlite__step_done(stmt));
7810 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7811 b->delete_dir_externals
7812 ? STMT_DELETE_EXTERNAL_REGISTATIONS
7813 : STMT_DELETE_FILE_EXTERNAL_REGISTATIONS));
7814 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7815 SVN_ERR(svn_sqlite__step_done(stmt));
7817 SVN_ERR(add_work_items(wcroot->sdb, b->work_items, scratch_pool));
7819 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
7820 b->conflict, scratch_pool));
7822 return SVN_NO_ERROR;
7825 static svn_error_t *
7826 op_delete_txn(void *baton,
7827 svn_wc__db_wcroot_t *wcroot,
7828 const char *local_relpath,
7829 apr_pool_t *scratch_pool)
7832 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
7833 SVN_ERR(delete_node(baton, wcroot, local_relpath, scratch_pool));
7834 return SVN_NO_ERROR;
7838 struct op_delete_many_baton_t {
7839 apr_array_header_t *rel_targets;
7840 svn_boolean_t delete_dir_externals;
7841 const svn_skel_t *work_items;
7842 } op_delete_many_baton_t;
7844 static svn_error_t *
7845 op_delete_many_txn(void *baton,
7846 svn_wc__db_wcroot_t *wcroot,
7847 const char *local_relpath,
7848 apr_pool_t *scratch_pool)
7850 struct op_delete_many_baton_t *odmb = baton;
7851 struct op_delete_baton_t odb;
7853 apr_pool_t *iterpool;
7855 odb.moved_to_relpath = NULL;
7856 odb.conflict = NULL;
7857 odb.work_items = NULL;
7858 odb.delete_dir_externals = odmb->delete_dir_externals;
7861 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
7862 iterpool = svn_pool_create(scratch_pool);
7863 for (i = 0; i < odmb->rel_targets->nelts; i++)
7865 const char *target_relpath = APR_ARRAY_IDX(odmb->rel_targets, i,
7869 svn_pool_clear(iterpool);
7870 SVN_ERR(delete_node(&odb, wcroot, target_relpath, iterpool));
7872 svn_pool_destroy(iterpool);
7874 SVN_ERR(add_work_items(wcroot->sdb, odmb->work_items, scratch_pool));
7876 return SVN_NO_ERROR;
7880 static svn_error_t *
7881 do_delete_notify(void *baton,
7882 svn_wc__db_wcroot_t *wcroot,
7883 svn_cancel_func_t cancel_func,
7885 svn_wc_notify_func2_t notify_func,
7887 apr_pool_t *scratch_pool)
7889 svn_sqlite__stmt_t *stmt;
7890 svn_boolean_t have_row;
7891 apr_pool_t *iterpool;
7893 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7894 STMT_SELECT_DELETE_LIST));
7895 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7897 iterpool = svn_pool_create(scratch_pool);
7900 const char *notify_relpath;
7901 const char *notify_abspath;
7903 svn_pool_clear(iterpool);
7905 notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7906 notify_abspath = svn_dirent_join(wcroot->abspath,
7910 notify_func(notify_baton,
7911 svn_wc_create_notify(notify_abspath,
7912 svn_wc_notify_delete,
7916 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7918 svn_pool_destroy(iterpool);
7920 SVN_ERR(svn_sqlite__reset(stmt));
7922 /* We only allow cancellation after notification for all deleted nodes
7923 * has happened. The nodes are already deleted so we should notify for
7926 SVN_ERR(cancel_func(cancel_baton));
7928 return SVN_NO_ERROR;
7933 svn_wc__db_op_delete(svn_wc__db_t *db,
7934 const char *local_abspath,
7935 const char *moved_to_abspath,
7936 svn_boolean_t delete_dir_externals,
7937 svn_skel_t *conflict,
7938 svn_skel_t *work_items,
7939 svn_cancel_func_t cancel_func,
7941 svn_wc_notify_func2_t notify_func,
7943 apr_pool_t *scratch_pool)
7945 svn_wc__db_wcroot_t *wcroot;
7946 svn_wc__db_wcroot_t *moved_to_wcroot;
7947 const char *local_relpath;
7948 const char *moved_to_relpath;
7949 struct op_delete_baton_t odb;
7951 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7953 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7955 scratch_pool, scratch_pool));
7956 VERIFY_USABLE_WCROOT(wcroot);
7958 if (moved_to_abspath)
7960 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&moved_to_wcroot,
7962 db, moved_to_abspath,
7965 VERIFY_USABLE_WCROOT(moved_to_wcroot);
7967 if (strcmp(wcroot->abspath, moved_to_wcroot->abspath) != 0)
7968 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
7969 _("Cannot move '%s' to '%s' because they "
7970 "are not in the same working copy"),
7971 svn_dirent_local_style(local_abspath,
7973 svn_dirent_local_style(moved_to_abspath,
7977 moved_to_relpath = NULL;
7979 odb.moved_to_relpath = moved_to_relpath;
7980 odb.conflict = conflict;
7981 odb.work_items = work_items;
7982 odb.delete_dir_externals = delete_dir_externals;
7986 /* Perform the deletion operation (transactionally), perform any
7987 notifications necessary, and then clean out our temporary tables. */
7989 SVN_ERR(with_finalization(wcroot, local_relpath,
7990 op_delete_txn, &odb,
7991 do_delete_notify, NULL,
7992 cancel_func, cancel_baton,
7993 notify_func, notify_baton,
7994 STMT_FINALIZE_DELETE,
7999 /* Avoid the trigger work */
8001 SVN_WC__DB_WITH_TXN(
8002 delete_node(&odb, wcroot, local_relpath, scratch_pool),
8006 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
8009 return SVN_NO_ERROR;
8014 svn_wc__db_op_delete_many(svn_wc__db_t *db,
8015 apr_array_header_t *targets,
8016 svn_boolean_t delete_dir_externals,
8017 const svn_skel_t *work_items,
8018 svn_cancel_func_t cancel_func,
8020 svn_wc_notify_func2_t notify_func,
8022 apr_pool_t *scratch_pool)
8024 svn_wc__db_wcroot_t *wcroot;
8025 const char *local_relpath;
8026 struct op_delete_many_baton_t odmb;
8028 apr_pool_t *iterpool;
8030 odmb.rel_targets = apr_array_make(scratch_pool, targets->nelts,
8031 sizeof(const char *));
8032 odmb.work_items = work_items;
8033 odmb.delete_dir_externals = delete_dir_externals;
8034 iterpool = svn_pool_create(scratch_pool);
8035 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8037 APR_ARRAY_IDX(targets, 0,
8039 scratch_pool, iterpool));
8040 VERIFY_USABLE_WCROOT(wcroot);
8041 for (i = 0; i < targets->nelts; i++)
8043 const char *local_abspath = APR_ARRAY_IDX(targets, i, const char*);
8044 svn_wc__db_wcroot_t *target_wcroot;
8046 svn_pool_clear(iterpool);
8048 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&target_wcroot,
8050 APR_ARRAY_IDX(targets, i,
8052 scratch_pool, iterpool));
8053 VERIFY_USABLE_WCROOT(target_wcroot);
8054 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8056 /* Assert that all targets are within the same working copy. */
8057 SVN_ERR_ASSERT(wcroot->wc_id == target_wcroot->wc_id);
8059 APR_ARRAY_PUSH(odmb.rel_targets, const char *) = local_relpath;
8060 SVN_ERR(flush_entries(target_wcroot, local_abspath, svn_depth_infinity,
8064 svn_pool_destroy(iterpool);
8066 /* Perform the deletion operation (transactionally), perform any
8067 notifications necessary, and then clean out our temporary tables. */
8068 return svn_error_trace(with_finalization(wcroot, wcroot->abspath,
8069 op_delete_many_txn, &odmb,
8070 do_delete_notify, NULL,
8071 cancel_func, cancel_baton,
8072 notify_func, notify_baton,
8073 STMT_FINALIZE_DELETE,
8078 /* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of
8079 DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */
8080 static svn_error_t *
8081 read_info(svn_wc__db_status_t *status,
8082 svn_node_kind_t *kind,
8083 svn_revnum_t *revision,
8084 const char **repos_relpath,
8085 apr_int64_t *repos_id,
8086 svn_revnum_t *changed_rev,
8087 apr_time_t *changed_date,
8088 const char **changed_author,
8090 const svn_checksum_t **checksum,
8091 const char **target,
8092 const char **original_repos_relpath,
8093 apr_int64_t *original_repos_id,
8094 svn_revnum_t *original_revision,
8095 svn_wc__db_lock_t **lock,
8096 svn_filesize_t *recorded_size,
8097 apr_time_t *recorded_time,
8098 const char **changelist,
8099 svn_boolean_t *conflicted,
8100 svn_boolean_t *op_root,
8101 svn_boolean_t *had_props,
8102 svn_boolean_t *props_mod,
8103 svn_boolean_t *have_base,
8104 svn_boolean_t *have_more_work,
8105 svn_boolean_t *have_work,
8106 svn_wc__db_wcroot_t *wcroot,
8107 const char *local_relpath,
8108 apr_pool_t *result_pool,
8109 apr_pool_t *scratch_pool)
8111 svn_sqlite__stmt_t *stmt_info;
8112 svn_sqlite__stmt_t *stmt_act;
8113 svn_boolean_t have_info;
8114 svn_boolean_t have_act;
8115 svn_error_t *err = NULL;
8117 /* Obtain the most likely to exist record first, to make sure we don't
8118 have to obtain the SQLite read-lock multiple times */
8119 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
8120 lock ? STMT_SELECT_NODE_INFO_WITH_LOCK
8121 : STMT_SELECT_NODE_INFO));
8122 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
8123 SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
8125 if (changelist || conflicted || props_mod)
8127 SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
8128 STMT_SELECT_ACTUAL_NODE));
8129 SVN_ERR(svn_sqlite__bindf(stmt_act, "is", wcroot->wc_id, local_relpath));
8130 SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
8141 svn_node_kind_t node_kind;
8143 op_depth = svn_sqlite__column_int(stmt_info, 0);
8144 node_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
8148 *status = svn_sqlite__column_token(stmt_info, 3, presence_map);
8150 if (op_depth != 0) /* WORKING */
8151 err = svn_error_compose_create(err,
8152 convert_to_working_status(status,
8162 *repos_id = INVALID_REPOS_ID;
8164 *revision = SVN_INVALID_REVNUM;
8166 /* Our path is implied by our parent somewhere up the tree.
8167 With the NULL value and status, the caller will know to
8168 search up the tree for the base of our path. */
8169 *repos_relpath = NULL;
8173 /* Fetch repository information. If we have a
8174 WORKING_NODE (and have been added), then the repository
8175 we're being added to will be dependent upon a parent. The
8176 caller can scan upwards to locate the repository. */
8177 repos_location_from_columns(repos_id, revision, repos_relpath,
8178 stmt_info, 1, 5, 2, result_pool);
8182 *changed_rev = svn_sqlite__column_revnum(stmt_info, 8);
8186 *changed_date = svn_sqlite__column_int64(stmt_info, 9);
8190 *changed_author = svn_sqlite__column_text(stmt_info, 10,
8195 *recorded_time = svn_sqlite__column_int64(stmt_info, 13);
8199 if (node_kind != svn_node_dir)
8201 *depth = svn_depth_unknown;
8205 *depth = svn_sqlite__column_token_null(stmt_info, 11, depth_map,
8211 if (node_kind != svn_node_file)
8218 err = svn_error_compose_create(
8219 err, svn_sqlite__column_checksum(checksum, stmt_info, 6,
8225 *recorded_size = get_recorded_size(stmt_info, 7);
8229 if (node_kind != svn_node_symlink)
8232 *target = svn_sqlite__column_text(stmt_info, 12, result_pool);
8237 *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8243 if (original_repos_id)
8244 *original_repos_id = INVALID_REPOS_ID;
8245 if (original_revision)
8246 *original_revision = SVN_INVALID_REVNUM;
8247 if (original_repos_relpath)
8248 *original_repos_relpath = NULL;
8252 repos_location_from_columns(original_repos_id,
8254 original_repos_relpath,
8255 stmt_info, 1, 5, 2, result_pool);
8259 *props_mod = have_act && !svn_sqlite__column_is_null(stmt_act, 1);
8263 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt_info, 14);
8270 !svn_sqlite__column_is_null(stmt_act, 2); /* conflict_data */
8273 *conflicted = FALSE;
8281 *lock = lock_from_columns(stmt_info, 17, 18, 19, 20, result_pool);
8285 *have_work = (op_depth != 0);
8289 *op_root = ((op_depth > 0)
8290 && (op_depth == relpath_depth(local_relpath)));
8293 if (have_base || have_more_work)
8296 *have_more_work = FALSE;
8298 while (!err && op_depth != 0)
8300 err = svn_sqlite__step(&have_info, stmt_info);
8302 if (err || !have_info)
8305 op_depth = svn_sqlite__column_int(stmt_info, 0);
8310 *have_more_work = TRUE;
8318 *have_base = (op_depth == 0);
8323 /* A row in ACTUAL_NODE should never exist without a corresponding
8324 node in BASE_NODE and/or WORKING_NODE unless it flags a tree conflict. */
8325 if (svn_sqlite__column_is_null(stmt_act, 2)) /* conflict_data */
8326 err = svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
8327 _("Corrupt data for '%s'"),
8328 path_for_error_message(wcroot, local_relpath,
8330 /* ### What should we return? Should we have a separate
8331 function for reading actual-only nodes? */
8333 /* As a safety measure, until we decide if we want to use
8334 read_info for actual-only nodes, make sure the caller asked
8335 for the conflict status. */
8336 SVN_ERR_ASSERT(conflicted);
8339 *status = svn_wc__db_status_normal; /* What! No it's not! */
8341 *kind = svn_node_unknown;
8343 *revision = SVN_INVALID_REVNUM;
8345 *repos_relpath = NULL;
8347 *repos_id = INVALID_REPOS_ID;
8349 *changed_rev = SVN_INVALID_REVNUM;
8353 *depth = svn_depth_unknown;
8358 if (original_repos_relpath)
8359 *original_repos_relpath = NULL;
8360 if (original_repos_id)
8361 *original_repos_id = INVALID_REPOS_ID;
8362 if (original_revision)
8363 *original_revision = SVN_INVALID_REVNUM;
8371 *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8383 *have_more_work = FALSE;
8389 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
8390 _("The node '%s' was not found."),
8391 path_for_error_message(wcroot, local_relpath,
8395 if (stmt_act != NULL)
8396 err = svn_error_compose_create(err, svn_sqlite__reset(stmt_act));
8398 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
8399 err = svn_error_quick_wrap(err,
8400 apr_psprintf(scratch_pool,
8401 "Error reading node '%s'",
8404 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info)));
8406 return SVN_NO_ERROR;
8411 svn_wc__db_read_info_internal(svn_wc__db_status_t *status,
8412 svn_node_kind_t *kind,
8413 svn_revnum_t *revision,
8414 const char **repos_relpath,
8415 apr_int64_t *repos_id,
8416 svn_revnum_t *changed_rev,
8417 apr_time_t *changed_date,
8418 const char **changed_author,
8420 const svn_checksum_t **checksum,
8421 const char **target,
8422 const char **original_repos_relpath,
8423 apr_int64_t *original_repos_id,
8424 svn_revnum_t *original_revision,
8425 svn_wc__db_lock_t **lock,
8426 svn_filesize_t *recorded_size,
8427 apr_time_t *recorded_time,
8428 const char **changelist,
8429 svn_boolean_t *conflicted,
8430 svn_boolean_t *op_root,
8431 svn_boolean_t *had_props,
8432 svn_boolean_t *props_mod,
8433 svn_boolean_t *have_base,
8434 svn_boolean_t *have_more_work,
8435 svn_boolean_t *have_work,
8436 svn_wc__db_wcroot_t *wcroot,
8437 const char *local_relpath,
8438 apr_pool_t *result_pool,
8439 apr_pool_t *scratch_pool)
8441 return svn_error_trace(
8442 read_info(status, kind, revision, repos_relpath, repos_id,
8443 changed_rev, changed_date, changed_author,
8444 depth, checksum, target, original_repos_relpath,
8445 original_repos_id, original_revision, lock,
8446 recorded_size, recorded_time, changelist, conflicted,
8447 op_root, had_props, props_mod,
8448 have_base, have_more_work, have_work,
8449 wcroot, local_relpath, result_pool, scratch_pool));
8454 svn_wc__db_read_info(svn_wc__db_status_t *status,
8455 svn_node_kind_t *kind,
8456 svn_revnum_t *revision,
8457 const char **repos_relpath,
8458 const char **repos_root_url,
8459 const char **repos_uuid,
8460 svn_revnum_t *changed_rev,
8461 apr_time_t *changed_date,
8462 const char **changed_author,
8464 const svn_checksum_t **checksum,
8465 const char **target,
8466 const char **original_repos_relpath,
8467 const char **original_root_url,
8468 const char **original_uuid,
8469 svn_revnum_t *original_revision,
8470 svn_wc__db_lock_t **lock,
8471 svn_filesize_t *recorded_size,
8472 apr_time_t *recorded_time,
8473 const char **changelist,
8474 svn_boolean_t *conflicted,
8475 svn_boolean_t *op_root,
8476 svn_boolean_t *have_props,
8477 svn_boolean_t *props_mod,
8478 svn_boolean_t *have_base,
8479 svn_boolean_t *have_more_work,
8480 svn_boolean_t *have_work,
8482 const char *local_abspath,
8483 apr_pool_t *result_pool,
8484 apr_pool_t *scratch_pool)
8486 svn_wc__db_wcroot_t *wcroot;
8487 const char *local_relpath;
8488 apr_int64_t repos_id, original_repos_id;
8490 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8492 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
8493 local_abspath, scratch_pool, scratch_pool));
8494 VERIFY_USABLE_WCROOT(wcroot);
8496 SVN_ERR(read_info(status, kind, revision, repos_relpath, &repos_id,
8497 changed_rev, changed_date, changed_author,
8498 depth, checksum, target, original_repos_relpath,
8499 &original_repos_id, original_revision, lock,
8500 recorded_size, recorded_time, changelist, conflicted,
8501 op_root, have_props, props_mod,
8502 have_base, have_more_work, have_work,
8503 wcroot, local_relpath, result_pool, scratch_pool));
8504 SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
8505 wcroot->sdb, repos_id, result_pool));
8506 SVN_ERR(svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
8507 wcroot->sdb, original_repos_id,
8510 return SVN_NO_ERROR;
8513 static svn_error_t *
8514 is_wclocked(svn_boolean_t *locked,
8515 svn_wc__db_wcroot_t *wcroot,
8516 const char *dir_relpath,
8517 apr_pool_t *scratch_pool);
8519 /* What we really want to store about a node. This relies on the
8520 offset of svn_wc__db_info_t being zero. */
8521 struct read_children_info_item_t
8523 struct svn_wc__db_info_t info;
8528 static svn_error_t *
8529 read_children_info(svn_wc__db_wcroot_t *wcroot,
8530 const char *dir_relpath,
8531 apr_hash_t *conflicts,
8533 apr_pool_t *result_pool,
8534 apr_pool_t *scratch_pool)
8536 svn_sqlite__stmt_t *stmt;
8537 svn_boolean_t have_row;
8538 const char *repos_root_url = NULL;
8539 const char *repos_uuid = NULL;
8540 apr_int64_t last_repos_id = INVALID_REPOS_ID;
8542 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8543 STMT_SELECT_NODE_CHILDREN_INFO));
8544 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
8545 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8549 /* CHILD item points to what we have about the node. We only provide
8550 CHILD->item to our caller. */
8551 struct read_children_info_item_t *child_item;
8552 const char *child_relpath = svn_sqlite__column_text(stmt, 19, NULL);
8553 const char *name = svn_relpath_basename(child_relpath, NULL);
8556 svn_boolean_t new_child;
8558 child_item = svn_hash_gets(nodes, name);
8563 child_item = apr_pcalloc(result_pool, sizeof(*child_item));
8567 op_depth = svn_sqlite__column_int(stmt, 0);
8569 /* Do we have new or better information? */
8570 if (new_child || op_depth > child_item->op_depth)
8572 struct svn_wc__db_info_t *child = &child_item->info;
8573 child_item->op_depth = op_depth;
8575 child->kind = svn_sqlite__column_token(stmt, 4, kind_map);
8577 child->status = svn_sqlite__column_token(stmt, 3, presence_map);
8580 if (child->status == svn_wc__db_status_incomplete)
8581 child->incomplete = TRUE;
8582 err = convert_to_working_status(&child->status, child->status);
8584 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8588 child->revnum = SVN_INVALID_REVNUM;
8590 child->revnum = svn_sqlite__column_revnum(stmt, 5);
8593 child->repos_relpath = NULL;
8595 child->repos_relpath = svn_sqlite__column_text(stmt, 2,
8598 if (op_depth != 0 || svn_sqlite__column_is_null(stmt, 1))
8600 child->repos_root_url = NULL;
8601 child->repos_uuid = NULL;
8605 const char *last_repos_root_url = NULL;
8607 apr_int64_t repos_id = svn_sqlite__column_int64(stmt, 1);
8608 if (!repos_root_url ||
8609 (last_repos_id != INVALID_REPOS_ID &&
8610 repos_id != last_repos_id))
8612 last_repos_root_url = repos_root_url;
8613 err = svn_wc__db_fetch_repos_info(&repos_root_url,
8615 wcroot->sdb, repos_id,
8618 SVN_ERR(svn_error_compose_create(err,
8619 svn_sqlite__reset(stmt)));
8622 if (last_repos_id == INVALID_REPOS_ID)
8623 last_repos_id = repos_id;
8625 /* Assume working copy is all one repos_id so that a
8626 single cached value is sufficient. */
8627 if (repos_id != last_repos_id)
8629 err= svn_error_createf(
8630 SVN_ERR_WC_DB_ERROR, NULL,
8631 _("The node '%s' comes from unexpected repository "
8632 "'%s', expected '%s'; if this node is a file "
8633 "external using the correct URL in the external "
8634 "definition can fix the problem, see issue #4087"),
8635 child_relpath, repos_root_url, last_repos_root_url);
8636 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
8638 child->repos_root_url = repos_root_url;
8639 child->repos_uuid = repos_uuid;
8642 child->changed_rev = svn_sqlite__column_revnum(stmt, 8);
8644 child->changed_date = svn_sqlite__column_int64(stmt, 9);
8646 child->changed_author = svn_sqlite__column_text(stmt, 10,
8649 if (child->kind != svn_node_dir)
8650 child->depth = svn_depth_unknown;
8653 child->depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
8656 SVN_ERR(is_wclocked(&child->locked, wcroot, child_relpath,
8660 child->recorded_time = svn_sqlite__column_int64(stmt, 13);
8661 child->recorded_size = get_recorded_size(stmt, 7);
8662 child->has_checksum = !svn_sqlite__column_is_null(stmt, 6);
8663 child->copied = op_depth > 0 && !svn_sqlite__column_is_null(stmt, 2);
8664 child->had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
8666 if (child->had_props)
8668 apr_hash_t *properties;
8669 err = svn_sqlite__column_properties(&properties, stmt, 14,
8670 scratch_pool, scratch_pool);
8672 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8674 child->special = (child->had_props
8675 && svn_hash_gets(properties, SVN_PROP_SPECIAL));
8679 child->op_root = FALSE;
8681 child->op_root = (op_depth == relpath_depth(child_relpath));
8683 svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
8688 child_item->info.have_base = TRUE;
8690 /* Get the lock info, available only at op_depth 0. */
8691 child_item->info.lock = lock_from_columns(stmt, 15, 16, 17, 18,
8694 /* FILE_EXTERNAL flag only on op_depth 0. */
8695 child_item->info.file_external = svn_sqlite__column_boolean(stmt,
8700 const char *moved_to_relpath;
8702 child_item->nr_layers++;
8703 child_item->info.have_more_work = (child_item->nr_layers > 1);
8705 /* Moved-to can only exist at op_depth > 0. */
8706 /* ### Should we really do this for every layer where op_depth > 0
8707 in undefined order? */
8708 moved_to_relpath = svn_sqlite__column_text(stmt, 21, NULL);
8709 if (moved_to_relpath)
8710 child_item->info.moved_to_abspath =
8711 svn_dirent_join(wcroot->abspath, moved_to_relpath, result_pool);
8713 /* Moved-here can only exist at op_depth > 0. */
8714 /* ### Should we really do this for every layer where op_depth > 0
8715 in undefined order? */
8716 child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
8719 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8722 SVN_ERR(svn_sqlite__reset(stmt));
8724 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8725 STMT_SELECT_ACTUAL_CHILDREN_INFO));
8726 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
8727 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8731 struct read_children_info_item_t *child_item;
8732 struct svn_wc__db_info_t *child;
8733 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8734 const char *name = svn_relpath_basename(child_relpath, NULL);
8736 child_item = svn_hash_gets(nodes, name);
8739 child_item = apr_pcalloc(result_pool, sizeof(*child_item));
8740 child_item->info.status = svn_wc__db_status_not_present;
8743 child = &child_item->info;
8745 child->changelist = svn_sqlite__column_text(stmt, 1, result_pool);
8747 child->props_mod = !svn_sqlite__column_is_null(stmt, 2);
8749 if (child->props_mod)
8752 apr_hash_t *properties;
8754 err = svn_sqlite__column_properties(&properties, stmt, 2,
8755 scratch_pool, scratch_pool);
8757 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8758 child->special = (NULL != svn_hash_gets(properties,
8763 child->conflicted = !svn_sqlite__column_is_null(stmt, 3); /* conflict */
8765 if (child->conflicted)
8766 svn_hash_sets(conflicts, apr_pstrdup(result_pool, name), "");
8768 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8771 SVN_ERR(svn_sqlite__reset(stmt));
8773 return SVN_NO_ERROR;
8777 svn_wc__db_read_children_info(apr_hash_t **nodes,
8778 apr_hash_t **conflicts,
8780 const char *dir_abspath,
8781 apr_pool_t *result_pool,
8782 apr_pool_t *scratch_pool)
8784 svn_wc__db_wcroot_t *wcroot;
8785 const char *dir_relpath;
8787 *conflicts = apr_hash_make(result_pool);
8788 *nodes = apr_hash_make(result_pool);
8789 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
8791 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
8793 scratch_pool, scratch_pool));
8794 VERIFY_USABLE_WCROOT(wcroot);
8796 SVN_WC__DB_WITH_TXN(
8797 read_children_info(wcroot, dir_relpath, *conflicts, *nodes,
8798 result_pool, scratch_pool),
8801 return SVN_NO_ERROR;
8805 svn_wc__db_read_pristine_info(svn_wc__db_status_t *status,
8806 svn_node_kind_t *kind,
8807 svn_revnum_t *changed_rev,
8808 apr_time_t *changed_date,
8809 const char **changed_author,
8810 svn_depth_t *depth, /* dirs only */
8811 const svn_checksum_t **checksum, /* files only */
8812 const char **target, /* symlinks only */
8813 svn_boolean_t *had_props,
8816 const char *local_abspath,
8817 apr_pool_t *result_pool,
8818 apr_pool_t *scratch_pool)
8820 svn_wc__db_wcroot_t *wcroot;
8821 const char *local_relpath;
8822 svn_sqlite__stmt_t *stmt;
8823 svn_boolean_t have_row;
8824 svn_error_t *err = NULL;
8826 svn_wc__db_status_t raw_status;
8827 svn_node_kind_t node_kind;
8829 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8831 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
8833 scratch_pool, scratch_pool));
8834 VERIFY_USABLE_WCROOT(wcroot);
8836 /* Obtain the most likely to exist record first, to make sure we don't
8837 have to obtain the SQLite read-lock multiple times */
8838 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8839 STMT_SELECT_NODE_INFO));
8840 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8841 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8845 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
8846 svn_sqlite__reset(stmt),
8847 _("The node '%s' was not found."),
8848 path_for_error_message(wcroot,
8853 op_depth = svn_sqlite__column_int(stmt, 0);
8854 raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
8856 if (op_depth > 0 && raw_status == svn_wc__db_status_base_deleted)
8858 SVN_ERR(svn_sqlite__step_row(stmt));
8860 op_depth = svn_sqlite__column_int(stmt, 0);
8861 raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
8864 node_kind = svn_sqlite__column_token(stmt, 4, kind_map);
8870 err = svn_error_compose_create(err,
8871 convert_to_working_status(
8876 *status = raw_status;
8884 *changed_rev = svn_sqlite__column_revnum(stmt, 8);
8888 *changed_date = svn_sqlite__column_int64(stmt, 9);
8892 *changed_author = svn_sqlite__column_text(stmt, 10,
8897 if (node_kind != svn_node_dir)
8899 *depth = svn_depth_unknown;
8903 *depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
8909 if (node_kind != svn_node_file)
8916 err2 = svn_sqlite__column_checksum(checksum, stmt, 6, result_pool);
8921 err = svn_error_compose_create(
8925 _("The node '%s' has a corrupt checksum value."),
8926 path_for_error_message(wcroot, local_relpath,
8935 if (node_kind != svn_node_symlink)
8938 *target = svn_sqlite__column_text(stmt, 12, result_pool);
8942 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
8946 if (raw_status == svn_wc__db_status_normal
8947 || raw_status == svn_wc__db_status_incomplete)
8949 SVN_ERR(svn_sqlite__column_properties(props, stmt, 14,
8950 result_pool, scratch_pool));
8952 *props = apr_hash_make(result_pool);
8956 assert(svn_sqlite__column_is_null(stmt, 14));
8961 return svn_error_trace(
8962 svn_error_compose_create(err,
8963 svn_sqlite__reset(stmt)));
8967 svn_wc__db_read_children_walker_info(apr_hash_t **nodes,
8969 const char *dir_abspath,
8970 apr_pool_t *result_pool,
8971 apr_pool_t *scratch_pool)
8973 svn_wc__db_wcroot_t *wcroot;
8974 const char *dir_relpath;
8975 svn_sqlite__stmt_t *stmt;
8976 svn_boolean_t have_row;
8978 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
8980 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
8982 scratch_pool, scratch_pool));
8983 VERIFY_USABLE_WCROOT(wcroot);
8985 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8986 STMT_SELECT_NODE_CHILDREN_WALKER_INFO));
8987 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
8988 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8990 *nodes = apr_hash_make(result_pool);
8993 struct svn_wc__db_walker_info_t *child;
8994 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8995 const char *name = svn_relpath_basename(child_relpath, NULL);
8996 int op_depth = svn_sqlite__column_int(stmt, 1);
8999 child = apr_palloc(result_pool, sizeof(*child));
9000 child->status = svn_sqlite__column_token(stmt, 2, presence_map);
9003 err = convert_to_working_status(&child->status, child->status);
9005 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9007 child->kind = svn_sqlite__column_token(stmt, 3, kind_map);
9008 svn_hash_sets(*nodes, apr_pstrdup(result_pool, name), child);
9010 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9013 SVN_ERR(svn_sqlite__reset(stmt));
9015 return SVN_NO_ERROR;
9019 svn_wc__db_read_node_install_info(const char **wcroot_abspath,
9020 const svn_checksum_t **sha1_checksum,
9021 apr_hash_t **pristine_props,
9022 apr_time_t *changed_date,
9024 const char *local_abspath,
9025 const char *wri_abspath,
9026 apr_pool_t *result_pool,
9027 apr_pool_t *scratch_pool)
9029 svn_wc__db_wcroot_t *wcroot;
9030 const char *local_relpath;
9031 svn_sqlite__stmt_t *stmt;
9032 svn_error_t *err = NULL;
9033 svn_boolean_t have_row;
9035 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9038 wri_abspath = local_abspath;
9040 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9041 wri_abspath, scratch_pool, scratch_pool));
9042 VERIFY_USABLE_WCROOT(wcroot);
9044 if (local_abspath != wri_abspath
9045 && strcmp(local_abspath, wri_abspath))
9047 if (!svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
9048 return svn_error_createf(
9049 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
9050 _("The node '%s' is not in working copy '%s'"),
9051 svn_dirent_local_style(local_abspath, scratch_pool),
9052 svn_dirent_local_style(wcroot->abspath, scratch_pool));
9054 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
9057 if (wcroot_abspath != NULL)
9058 *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
9060 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9061 STMT_SELECT_NODE_INFO));
9063 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9065 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9069 if (!err && sha1_checksum)
9070 err = svn_sqlite__column_checksum(sha1_checksum, stmt, 6, result_pool);
9072 if (!err && pristine_props)
9074 err = svn_sqlite__column_properties(pristine_props, stmt, 14,
9075 result_pool, scratch_pool);
9076 /* Null means no props (assuming presence normal or incomplete). */
9077 if (*pristine_props == NULL)
9078 *pristine_props = apr_hash_make(result_pool);
9082 *changed_date = svn_sqlite__column_int64(stmt, 9);
9085 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9086 svn_sqlite__reset(stmt),
9087 _("The node '%s' is not installable"),
9088 svn_dirent_local_style(local_abspath,
9091 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9093 return SVN_NO_ERROR;
9098 /* The body of svn_wc__db_read_url().
9100 static svn_error_t *
9101 read_url_txn(const char **url,
9102 svn_wc__db_wcroot_t *wcroot,
9103 const char *local_relpath,
9104 apr_pool_t *result_pool,
9105 apr_pool_t *scratch_pool)
9107 svn_wc__db_status_t status;
9108 const char *repos_relpath;
9109 const char *repos_root_url;
9110 apr_int64_t repos_id;
9111 svn_boolean_t have_base;
9113 SVN_ERR(read_info(&status, NULL, NULL, &repos_relpath, &repos_id, NULL,
9114 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9115 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9116 &have_base, NULL, NULL,
9117 wcroot, local_relpath, scratch_pool, scratch_pool));
9119 if (repos_relpath == NULL)
9121 if (status == svn_wc__db_status_added)
9123 SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id, NULL,
9124 NULL, NULL, NULL, NULL, NULL,
9125 wcroot, local_relpath,
9126 scratch_pool, scratch_pool));
9128 else if (status == svn_wc__db_status_deleted)
9130 const char *base_del_relpath;
9131 const char *work_del_relpath;
9133 SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
9140 if (base_del_relpath)
9142 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
9147 NULL, NULL, NULL, NULL,
9153 repos_relpath = svn_relpath_join(
9155 svn_dirent_skip_ancestor(base_del_relpath,
9161 /* The parent of the WORKING delete, must be an addition */
9162 const char *work_relpath = NULL;
9164 /* work_del_relpath should not be NULL. However, we have
9165 * observed instances where that assumption was not met.
9166 * Bail out in that case instead of crashing with a segfault.
9168 SVN_ERR_ASSERT(work_del_relpath != NULL);
9169 work_relpath = svn_relpath_dirname(work_del_relpath,
9172 SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id,
9173 NULL, NULL, NULL, NULL, NULL, NULL,
9174 wcroot, work_relpath,
9175 scratch_pool, scratch_pool));
9177 repos_relpath = svn_relpath_join(
9179 svn_dirent_skip_ancestor(work_relpath,
9184 else if (status == svn_wc__db_status_excluded)
9186 const char *parent_relpath;
9190 /* Set 'url' to the *full URL* of the parent WC dir,
9191 * and 'name' to the *single path component* that is the
9192 * basename of this WC directory, so that joining them will result
9193 * in the correct full URL. */
9194 svn_relpath_split(&parent_relpath, &name, local_relpath,
9196 SVN_ERR(read_url_txn(&url2, wcroot, parent_relpath,
9197 scratch_pool, scratch_pool));
9199 *url = svn_path_url_add_component2(url2, name, result_pool);
9201 return SVN_NO_ERROR;
9205 /* All working statee are explicitly handled and all base statee
9206 have a repos_relpath */
9207 SVN_ERR_MALFUNCTION();
9211 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot->sdb,
9212 repos_id, scratch_pool));
9214 SVN_ERR_ASSERT(repos_root_url != NULL && repos_relpath != NULL);
9215 *url = svn_path_url_add_component2(repos_root_url, repos_relpath,
9218 return SVN_NO_ERROR;
9223 svn_wc__db_read_url(const char **url,
9225 const char *local_abspath,
9226 apr_pool_t *result_pool,
9227 apr_pool_t *scratch_pool)
9229 svn_wc__db_wcroot_t *wcroot;
9230 const char *local_relpath;
9232 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9234 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9236 scratch_pool, scratch_pool));
9237 VERIFY_USABLE_WCROOT(wcroot);
9239 SVN_WC__DB_WITH_TXN(read_url_txn(url, wcroot, local_relpath,
9240 result_pool, scratch_pool),
9243 return SVN_NO_ERROR;
9247 /* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
9248 a hash table mapping <tt>char *</tt> names onto svn_string_t *
9249 values for any properties of immediate or recursive child nodes of
9250 LOCAL_ABSPATH, the actual query being determined by STMT_IDX.
9251 If FILES_ONLY is true, only report properties for file child nodes.
9252 Check for cancellation between calls of RECEIVER_FUNC.
9254 typedef struct cache_props_baton_t
9257 svn_boolean_t pristine;
9258 const apr_array_header_t *changelists;
9259 svn_cancel_func_t cancel_func;
9261 } cache_props_baton_t;
9264 static svn_error_t *
9265 cache_props_recursive(void *cb_baton,
9266 svn_wc__db_wcroot_t *wcroot,
9267 const char *local_relpath,
9268 apr_pool_t *scratch_pool)
9270 cache_props_baton_t *baton = cb_baton;
9271 svn_sqlite__stmt_t *stmt;
9274 SVN_ERR(populate_targets_tree(wcroot, local_relpath, baton->depth,
9275 baton->changelists, scratch_pool));
9277 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
9278 STMT_CREATE_TARGET_PROP_CACHE));
9280 if (baton->pristine)
9281 stmt_idx = STMT_CACHE_TARGET_PRISTINE_PROPS;
9283 stmt_idx = STMT_CACHE_TARGET_PROPS;
9285 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
9286 SVN_ERR(svn_sqlite__bind_int64(stmt, 1, wcroot->wc_id));
9287 SVN_ERR(svn_sqlite__step_done(stmt));
9289 return SVN_NO_ERROR;
9294 svn_wc__db_read_props_streamily(svn_wc__db_t *db,
9295 const char *local_abspath,
9297 svn_boolean_t pristine,
9298 const apr_array_header_t *changelists,
9299 svn_wc__proplist_receiver_t receiver_func,
9300 void *receiver_baton,
9301 svn_cancel_func_t cancel_func,
9303 apr_pool_t *scratch_pool)
9305 svn_wc__db_wcroot_t *wcroot;
9306 const char *local_relpath;
9307 svn_sqlite__stmt_t *stmt;
9308 cache_props_baton_t baton;
9309 svn_boolean_t have_row;
9310 apr_pool_t *iterpool;
9311 svn_error_t *err = NULL;
9313 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9314 SVN_ERR_ASSERT(receiver_func);
9315 SVN_ERR_ASSERT((depth == svn_depth_files) ||
9316 (depth == svn_depth_immediates) ||
9317 (depth == svn_depth_infinity));
9319 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
9321 scratch_pool, scratch_pool));
9322 VERIFY_USABLE_WCROOT(wcroot);
9324 baton.depth = depth;
9325 baton.pristine = pristine;
9326 baton.changelists = changelists;
9327 baton.cancel_func = cancel_func;
9328 baton.cancel_baton = cancel_baton;
9330 SVN_ERR(with_finalization(wcroot, local_relpath,
9331 cache_props_recursive, &baton,
9333 cancel_func, cancel_baton,
9335 STMT_DROP_TARGETS_LIST,
9338 iterpool = svn_pool_create(scratch_pool);
9340 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9341 STMT_SELECT_ALL_TARGET_PROP_CACHE));
9342 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9343 while (!err && have_row)
9347 svn_pool_clear(iterpool);
9349 SVN_ERR(svn_sqlite__column_properties(&props, stmt, 1, iterpool,
9352 /* see if someone wants to cancel this operation. */
9354 err = cancel_func(cancel_baton);
9356 if (!err && props && apr_hash_count(props) != 0)
9358 const char *child_relpath;
9359 const char *child_abspath;
9361 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9362 child_abspath = svn_dirent_join(wcroot->abspath,
9363 child_relpath, iterpool);
9365 err = receiver_func(receiver_baton, child_abspath, props, iterpool);
9368 err = svn_error_compose_create(err, svn_sqlite__step(&have_row, stmt));
9371 err = svn_error_compose_create(err, svn_sqlite__reset(stmt));
9373 svn_pool_destroy(iterpool);
9375 SVN_ERR(svn_error_compose_create(
9377 svn_sqlite__exec_statements(wcroot->sdb,
9378 STMT_DROP_TARGET_PROP_CACHE)));
9379 return SVN_NO_ERROR;
9383 /* Helper for svn_wc__db_read_props().
9385 static svn_error_t *
9386 db_read_props(apr_hash_t **props,
9387 svn_wc__db_wcroot_t *wcroot,
9388 const char *local_relpath,
9389 apr_pool_t *result_pool,
9390 apr_pool_t *scratch_pool)
9392 svn_sqlite__stmt_t *stmt;
9393 svn_boolean_t have_row;
9394 svn_error_t *err = NULL;
9396 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9397 STMT_SELECT_ACTUAL_PROPS));
9398 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9399 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9401 if (have_row && !svn_sqlite__column_is_null(stmt, 0))
9403 err = svn_sqlite__column_properties(props, stmt, 0,
9404 result_pool, scratch_pool);
9409 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9412 return SVN_NO_ERROR;
9414 /* No local changes. Return the pristine props for this node. */
9415 SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, FALSE,
9416 result_pool, scratch_pool));
9419 /* Pristine properties are not defined for this node.
9420 ### we need to determine whether this node is in a state that
9421 ### allows for ACTUAL properties (ie. not deleted). for now,
9422 ### just say all nodes, no matter the state, have at least an
9423 ### empty set of props. */
9424 *props = apr_hash_make(result_pool);
9427 return SVN_NO_ERROR;
9432 svn_wc__db_read_props(apr_hash_t **props,
9434 const char *local_abspath,
9435 apr_pool_t *result_pool,
9436 apr_pool_t *scratch_pool)
9438 svn_wc__db_wcroot_t *wcroot;
9439 const char *local_relpath;
9441 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9443 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9444 local_abspath, scratch_pool, scratch_pool));
9445 VERIFY_USABLE_WCROOT(wcroot);
9447 SVN_WC__DB_WITH_TXN(db_read_props(props, wcroot, local_relpath,
9448 result_pool, scratch_pool),
9451 return SVN_NO_ERROR;
9455 static svn_error_t *
9456 db_read_pristine_props(apr_hash_t **props,
9457 svn_wc__db_wcroot_t *wcroot,
9458 const char *local_relpath,
9459 svn_boolean_t deleted_ok,
9460 apr_pool_t *result_pool,
9461 apr_pool_t *scratch_pool)
9463 svn_sqlite__stmt_t *stmt;
9464 svn_boolean_t have_row;
9465 svn_wc__db_status_t presence;
9467 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_NODE_PROPS));
9468 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9470 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9474 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9475 svn_sqlite__reset(stmt),
9476 _("The node '%s' was not found."),
9477 path_for_error_message(wcroot,
9483 /* Examine the presence: */
9484 presence = svn_sqlite__column_token(stmt, 1, presence_map);
9486 /* For "base-deleted", it is obvious the pristine props are located
9487 below the current node. Fetch the NODE from the next record. */
9488 if (presence == svn_wc__db_status_base_deleted && deleted_ok)
9490 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9492 SVN_ERR_ASSERT(have_row);
9494 presence = svn_sqlite__column_token(stmt, 1, presence_map);
9497 /* normal or copied: Fetch properties (during update we want
9498 properties for incomplete as well) */
9499 if (presence == svn_wc__db_status_normal
9500 || presence == svn_wc__db_status_incomplete)
9504 err = svn_sqlite__column_properties(props, stmt, 0, result_pool,
9506 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9509 *props = apr_hash_make(result_pool);
9511 return SVN_NO_ERROR;
9514 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
9515 svn_sqlite__reset(stmt),
9516 _("The node '%s' has a status that"
9517 " has no properties."),
9518 path_for_error_message(wcroot,
9525 svn_wc__db_read_pristine_props(apr_hash_t **props,
9527 const char *local_abspath,
9528 apr_pool_t *result_pool,
9529 apr_pool_t *scratch_pool)
9531 svn_wc__db_wcroot_t *wcroot;
9532 const char *local_relpath;
9534 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9536 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9537 local_abspath, scratch_pool, scratch_pool));
9538 VERIFY_USABLE_WCROOT(wcroot);
9540 SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, TRUE,
9541 result_pool, scratch_pool));
9542 return SVN_NO_ERROR;
9546 svn_wc__db_prop_retrieve_recursive(apr_hash_t **values,
9548 const char *local_abspath,
9549 const char *propname,
9550 apr_pool_t *result_pool,
9551 apr_pool_t *scratch_pool)
9553 svn_wc__db_wcroot_t *wcroot;
9554 const char *local_relpath;
9555 svn_sqlite__stmt_t *stmt;
9556 svn_boolean_t have_row;
9557 apr_pool_t *iterpool;
9559 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9561 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9562 local_abspath, scratch_pool, scratch_pool));
9563 VERIFY_USABLE_WCROOT(wcroot);
9565 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9566 STMT_SELECT_CURRENT_PROPS_RECURSIVE));
9567 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9569 *values = apr_hash_make(result_pool);
9571 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9572 iterpool = svn_pool_create(scratch_pool);
9575 apr_hash_t *node_props;
9576 svn_string_t *value;
9578 svn_pool_clear(iterpool);
9580 SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 0,
9581 iterpool, iterpool));
9584 ? svn_hash_gets(node_props, propname)
9589 svn_hash_sets(*values,
9590 svn_dirent_join(wcroot->abspath,
9591 svn_sqlite__column_text(stmt, 1, NULL),
9593 svn_string_dup(value, result_pool));
9596 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9599 svn_pool_destroy(iterpool);
9601 return svn_error_trace(svn_sqlite__reset(stmt));
9604 /* The body of svn_wc__db_read_cached_iprops(). */
9605 static svn_error_t *
9606 db_read_cached_iprops(apr_array_header_t **iprops,
9607 svn_wc__db_wcroot_t *wcroot,
9608 const char *local_relpath,
9609 apr_pool_t *result_pool,
9610 apr_pool_t *scratch_pool)
9612 svn_sqlite__stmt_t *stmt;
9613 svn_boolean_t have_row;
9615 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_IPROPS));
9616 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9617 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9621 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9622 svn_sqlite__reset(stmt),
9623 _("The node '%s' was not found."),
9624 path_for_error_message(wcroot, local_relpath,
9628 SVN_ERR(svn_sqlite__column_iprops(iprops, stmt, 0,
9629 result_pool, scratch_pool));
9631 SVN_ERR(svn_sqlite__reset(stmt));
9633 return SVN_NO_ERROR;
9637 svn_wc__db_read_cached_iprops(apr_array_header_t **iprops,
9639 const char *local_abspath,
9640 apr_pool_t *result_pool,
9641 apr_pool_t *scratch_pool)
9643 svn_wc__db_wcroot_t *wcroot;
9644 const char *local_relpath;
9646 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9648 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
9650 scratch_pool, scratch_pool));
9651 VERIFY_USABLE_WCROOT(wcroot);
9653 /* Don't use with_txn yet, as we perform just a single transaction */
9654 SVN_ERR(db_read_cached_iprops(iprops, wcroot, local_relpath,
9655 result_pool, scratch_pool));
9659 *iprops = apr_array_make(result_pool, 0,
9660 sizeof(svn_prop_inherited_item_t *));
9663 return SVN_NO_ERROR;
9666 /* Remove all prop name value pairs from PROP_HASH where the property
9667 name is not PROPNAME. */
9669 filter_unwanted_props(apr_hash_t *prop_hash,
9670 const char * propname,
9671 apr_pool_t *scratch_pool)
9673 apr_hash_index_t *hi;
9675 for (hi = apr_hash_first(scratch_pool, prop_hash);
9677 hi = apr_hash_next(hi))
9679 const char *ipropname = svn__apr_hash_index_key(hi);
9681 if (strcmp(ipropname, propname) != 0)
9682 svn_hash_sets(prop_hash, ipropname, NULL);
9687 /* Get the changed properties as stored in the ACTUAL table */
9688 static svn_error_t *
9689 db_get_changed_props(apr_hash_t **actual_props,
9690 svn_wc__db_wcroot_t *wcroot,
9691 const char *local_relpath,
9692 apr_pool_t *result_pool,
9693 apr_pool_t *scratch_pool)
9695 svn_sqlite__stmt_t *stmt;
9696 svn_boolean_t have_row;
9697 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9698 STMT_SELECT_ACTUAL_PROPS));
9699 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9700 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9702 if (have_row && !svn_sqlite__column_is_null(stmt, 0))
9703 SVN_ERR(svn_sqlite__column_properties(actual_props, stmt, 0,
9704 result_pool, scratch_pool));
9706 *actual_props = NULL; /* Cached when we read that record */
9708 return svn_error_trace(svn_sqlite__reset(stmt));
9711 /* The body of svn_wc__db_read_inherited_props(). */
9712 static svn_error_t *
9713 db_read_inherited_props(apr_array_header_t **inherited_props,
9714 apr_hash_t **actual_props,
9715 svn_wc__db_wcroot_t *wcroot,
9716 const char *local_relpath,
9717 const char *propname,
9718 apr_pool_t *result_pool,
9719 apr_pool_t *scratch_pool)
9722 apr_array_header_t *cached_iprops = NULL;
9723 apr_array_header_t *iprops;
9724 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
9725 svn_sqlite__stmt_t *stmt;
9726 const char *relpath;
9727 const char *expected_parent_repos_relpath = NULL;
9728 const char *parent_relpath;
9730 iprops = apr_array_make(result_pool, 1,
9731 sizeof(svn_prop_inherited_item_t *));
9732 *inherited_props = iprops;
9735 *actual_props = NULL;
9737 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9738 STMT_SELECT_NODE_INFO));
9740 relpath = local_relpath;
9742 /* Walk up to the root of the WC looking for inherited properties. When we
9743 reach the WC root also check for cached inherited properties. */
9744 for (relpath = local_relpath; relpath; relpath = parent_relpath)
9746 svn_boolean_t have_row;
9748 svn_wc__db_status_t status;
9749 apr_hash_t *node_props;
9751 parent_relpath = relpath[0] ? svn_relpath_dirname(relpath, scratch_pool)
9754 svn_pool_clear(iterpool);
9756 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, relpath));
9758 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9761 return svn_error_createf(
9762 SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
9763 _("The node '%s' was not found."),
9764 path_for_error_message(wcroot, relpath,
9767 op_depth = svn_sqlite__column_int(stmt, 0);
9769 status = svn_sqlite__column_token(stmt, 3, presence_map);
9771 if (status != svn_wc__db_status_normal
9772 && status != svn_wc__db_status_incomplete)
9773 return svn_error_createf(
9774 SVN_ERR_WC_PATH_UNEXPECTED_STATUS, svn_sqlite__reset(stmt),
9775 _("The node '%s' has a status that has no properties."),
9776 path_for_error_message(wcroot, relpath,
9781 /* WORKING node. Nothing to check */
9783 else if (expected_parent_repos_relpath)
9785 const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
9787 if (strcmp(expected_parent_repos_relpath, repos_relpath) != 0)
9789 /* The child of this node has a different parent than this node
9790 (It is "switched"), so we can stop here. Note that switched
9791 with the same parent is not interesting for us here. */
9792 SVN_ERR(svn_sqlite__reset(stmt));
9796 expected_parent_repos_relpath =
9797 svn_relpath_dirname(expected_parent_repos_relpath, scratch_pool);
9801 const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
9803 expected_parent_repos_relpath =
9804 svn_relpath_dirname(repos_relpath, scratch_pool);
9808 && !svn_sqlite__column_is_null(stmt, 16))
9810 /* The node contains a cache. No reason to look further */
9811 SVN_ERR(svn_sqlite__column_iprops(&cached_iprops, stmt, 16,
9812 result_pool, iterpool));
9814 parent_relpath = NULL; /* Stop after this */
9817 SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 14,
9818 iterpool, iterpool));
9820 SVN_ERR(svn_sqlite__reset(stmt));
9822 /* If PARENT_ABSPATH is a parent of LOCAL_ABSPATH, then LOCAL_ABSPATH
9823 can inherit properties from it. */
9824 if (relpath != local_relpath)
9826 apr_hash_t *changed_props;
9828 SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
9829 result_pool, iterpool));
9832 node_props = changed_props;
9833 else if (node_props)
9834 node_props = svn_prop_hash_dup(node_props, result_pool);
9836 if (node_props && apr_hash_count(node_props))
9838 /* If we only want PROPNAME filter out any other properties. */
9840 filter_unwanted_props(node_props, propname, iterpool);
9842 if (apr_hash_count(node_props))
9844 svn_prop_inherited_item_t *iprop_elt =
9845 apr_pcalloc(result_pool,
9846 sizeof(svn_prop_inherited_item_t));
9847 iprop_elt->path_or_url = svn_dirent_join(wcroot->abspath,
9851 iprop_elt->prop_hash = node_props;
9852 /* Build the output array in depth-first order. */
9853 svn_sort__array_insert(&iprop_elt, iprops, 0);
9857 else if (actual_props)
9859 apr_hash_t *changed_props;
9861 SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
9862 result_pool, iterpool));
9865 *actual_props = changed_props;
9866 else if (node_props)
9867 *actual_props = svn_prop_hash_dup(node_props, result_pool);
9873 for (i = cached_iprops->nelts - 1; i >= 0; i--)
9875 svn_prop_inherited_item_t *cached_iprop =
9876 APR_ARRAY_IDX(cached_iprops, i, svn_prop_inherited_item_t *);
9878 /* An empty property hash in the iprops cache means there are no
9879 inherited properties. */
9880 if (apr_hash_count(cached_iprop->prop_hash) == 0)
9884 filter_unwanted_props(cached_iprop->prop_hash, propname,
9887 /* If we didn't filter everything then keep this iprop. */
9888 if (apr_hash_count(cached_iprop->prop_hash))
9889 svn_sort__array_insert(&cached_iprop, iprops, 0);
9893 if (actual_props && !*actual_props)
9894 *actual_props = apr_hash_make(result_pool);
9896 svn_pool_destroy(iterpool);
9897 return SVN_NO_ERROR;
9901 svn_wc__db_read_inherited_props(apr_array_header_t **iprops,
9902 apr_hash_t **actual_props,
9904 const char *local_abspath,
9905 const char *propname,
9906 apr_pool_t *result_pool,
9907 apr_pool_t *scratch_pool)
9909 svn_wc__db_wcroot_t *wcroot;
9910 const char *local_relpath;
9912 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9914 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
9916 scratch_pool, scratch_pool));
9917 VERIFY_USABLE_WCROOT(wcroot);
9919 SVN_WC__DB_WITH_TXN(db_read_inherited_props(iprops, actual_props,
9920 wcroot, local_relpath, propname,
9921 result_pool, scratch_pool),
9924 return SVN_NO_ERROR;
9927 /* The body of svn_wc__db_get_children_with_cached_iprops().
9929 static svn_error_t *
9930 get_children_with_cached_iprops(apr_hash_t **iprop_paths,
9931 svn_wc__db_wcroot_t *wcroot,
9932 const char *local_relpath,
9934 apr_pool_t *result_pool,
9935 apr_pool_t *scratch_pool)
9937 svn_sqlite__stmt_t *stmt;
9938 svn_boolean_t have_row;
9940 *iprop_paths = apr_hash_make(result_pool);
9942 /* First check if LOCAL_RELPATH itself has iprops */
9943 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9944 STMT_SELECT_IPROPS_NODE));
9945 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9946 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9950 const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
9952 const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
9955 svn_hash_sets(*iprop_paths, abspath_with_cache,
9956 svn_sqlite__column_text(stmt, 1, result_pool));
9958 SVN_ERR(svn_sqlite__reset(stmt));
9960 if (depth == svn_depth_empty)
9961 return SVN_NO_ERROR;
9963 /* Now fetch information for children or all descendants */
9964 if (depth == svn_depth_files
9965 || depth == svn_depth_immediates)
9967 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9968 STMT_SELECT_IPROPS_CHILDREN));
9970 else /* Default to svn_depth_infinity. */
9972 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9973 STMT_SELECT_IPROPS_RECURSIVE));
9976 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9977 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9981 const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
9983 const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
9986 svn_hash_sets(*iprop_paths, abspath_with_cache,
9987 svn_sqlite__column_text(stmt, 1, result_pool));
9988 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9991 SVN_ERR(svn_sqlite__reset(stmt));
9993 /* For depth files we should filter non files */
9994 if (depth == svn_depth_files)
9996 apr_hash_index_t *hi;
9997 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
9999 for (hi = apr_hash_first(scratch_pool, *iprop_paths);
10001 hi = apr_hash_next(hi))
10003 const char *child_abspath = svn__apr_hash_index_key(hi);
10004 const char *child_relpath;
10005 svn_node_kind_t child_kind;
10007 svn_pool_clear(iterpool);
10009 child_relpath = svn_dirent_is_child(local_relpath, child_abspath,
10012 if (! child_relpath)
10014 continue; /* local_relpath itself */
10017 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &child_kind, NULL,
10018 NULL, NULL, NULL, NULL,
10019 NULL, NULL, NULL, NULL,
10020 NULL, NULL, NULL, NULL,
10021 wcroot, child_relpath,
10025 /* Filter if not a file */
10026 if (child_kind != svn_node_file)
10028 svn_hash_sets(*iprop_paths, child_abspath, NULL);
10032 svn_pool_destroy(iterpool);
10035 return SVN_NO_ERROR;
10039 svn_wc__db_get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10041 const char *local_abspath,
10043 apr_pool_t *result_pool,
10044 apr_pool_t *scratch_pool)
10046 svn_wc__db_wcroot_t *wcroot;
10047 const char *local_relpath;
10049 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10051 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10052 local_abspath, scratch_pool,
10054 VERIFY_USABLE_WCROOT(wcroot);
10056 SVN_WC__DB_WITH_TXN(
10057 get_children_with_cached_iprops(iprop_paths, wcroot, local_relpath,
10058 depth, result_pool, scratch_pool),
10061 return SVN_NO_ERROR;
10065 svn_wc__db_read_children_of_working_node(const apr_array_header_t **children,
10067 const char *local_abspath,
10068 apr_pool_t *result_pool,
10069 apr_pool_t *scratch_pool)
10071 svn_wc__db_wcroot_t *wcroot;
10072 const char *local_relpath;
10074 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10076 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10078 scratch_pool, scratch_pool));
10079 VERIFY_USABLE_WCROOT(wcroot);
10081 return gather_children2(children, wcroot, local_relpath,
10082 result_pool, scratch_pool);
10085 /* Helper for svn_wc__db_node_check_replace().
10087 static svn_error_t *
10088 check_replace_txn(svn_boolean_t *is_replace_root_p,
10089 svn_boolean_t *base_replace_p,
10090 svn_boolean_t *is_replace_p,
10091 svn_wc__db_wcroot_t *wcroot,
10092 const char *local_relpath,
10093 apr_pool_t *scratch_pool)
10095 svn_sqlite__stmt_t *stmt;
10096 svn_boolean_t have_row;
10097 svn_boolean_t is_replace = FALSE;
10098 int replaced_op_depth;
10099 svn_wc__db_status_t replaced_status;
10101 /* Our caller initialized the output values to FALSE */
10103 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10104 STMT_SELECT_NODE_INFO));
10106 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10108 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10111 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10112 svn_sqlite__reset(stmt),
10113 _("The node '%s' was not found."),
10114 path_for_error_message(wcroot, local_relpath,
10118 svn_wc__db_status_t status;
10120 status = svn_sqlite__column_token(stmt, 3, presence_map);
10122 if (status != svn_wc__db_status_normal)
10123 return svn_error_trace(svn_sqlite__reset(stmt));
10126 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10129 return svn_error_trace(svn_sqlite__reset(stmt));
10131 replaced_status = svn_sqlite__column_token(stmt, 3, presence_map);
10133 /* If the layer below the add describes a not present or a deleted node,
10134 this is not a replacement. Deleted can only occur if an ancestor is
10135 the delete root. */
10136 if (replaced_status != svn_wc__db_status_not_present
10137 && replaced_status != svn_wc__db_status_excluded
10138 && replaced_status != svn_wc__db_status_server_excluded
10139 && replaced_status != svn_wc__db_status_base_deleted)
10143 *is_replace_p = TRUE;
10146 replaced_op_depth = svn_sqlite__column_int(stmt, 0);
10148 if (base_replace_p)
10150 int op_depth = svn_sqlite__column_int(stmt, 0);
10152 while (op_depth != 0 && have_row)
10154 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10157 op_depth = svn_sqlite__column_int(stmt, 0);
10160 if (have_row && op_depth == 0)
10162 svn_wc__db_status_t base_status;
10164 base_status = svn_sqlite__column_token(stmt, 3, presence_map);
10166 *base_replace_p = (base_status != svn_wc__db_status_not_present);
10170 SVN_ERR(svn_sqlite__reset(stmt));
10172 if (!is_replace_root_p || !is_replace)
10173 return SVN_NO_ERROR;
10175 if (replaced_status != svn_wc__db_status_base_deleted)
10177 int parent_op_depth;
10179 /* Check the current op-depth of the parent to see if we are a replacement
10181 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
10182 svn_relpath_dirname(local_relpath,
10185 SVN_ERR(svn_sqlite__step_row(stmt)); /* Parent must exist as 'normal' */
10187 parent_op_depth = svn_sqlite__column_int(stmt, 0);
10189 if (parent_op_depth >= replaced_op_depth)
10191 /* Did we replace inside our directory? */
10193 *is_replace_root_p = (parent_op_depth == replaced_op_depth);
10194 SVN_ERR(svn_sqlite__reset(stmt));
10195 return SVN_NO_ERROR;
10198 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10201 parent_op_depth = svn_sqlite__column_int(stmt, 0);
10203 SVN_ERR(svn_sqlite__reset(stmt));
10206 *is_replace_root_p = TRUE; /* Parent is no replacement */
10207 else if (parent_op_depth < replaced_op_depth)
10208 *is_replace_root_p = TRUE; /* Parent replaces a lower layer */
10209 /*else // No replacement root */
10212 return SVN_NO_ERROR;
10216 svn_wc__db_node_check_replace(svn_boolean_t *is_replace_root,
10217 svn_boolean_t *base_replace,
10218 svn_boolean_t *is_replace,
10220 const char *local_abspath,
10221 apr_pool_t *scratch_pool)
10223 svn_wc__db_wcroot_t *wcroot;
10224 const char *local_relpath;
10226 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10228 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10230 scratch_pool, scratch_pool));
10231 VERIFY_USABLE_WCROOT(wcroot);
10233 if (is_replace_root)
10234 *is_replace_root = FALSE;
10236 *base_replace = FALSE;
10238 *is_replace = FALSE;
10240 if (local_relpath[0] == '\0')
10241 return SVN_NO_ERROR; /* Working copy root can't be replaced */
10243 SVN_WC__DB_WITH_TXN(
10244 check_replace_txn(is_replace_root, base_replace, is_replace,
10245 wcroot, local_relpath, scratch_pool),
10248 return SVN_NO_ERROR;
10252 svn_wc__db_read_children(const apr_array_header_t **children,
10254 const char *local_abspath,
10255 apr_pool_t *result_pool,
10256 apr_pool_t *scratch_pool)
10258 svn_wc__db_wcroot_t *wcroot;
10259 const char *local_relpath;
10261 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10263 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10265 scratch_pool, scratch_pool));
10266 VERIFY_USABLE_WCROOT(wcroot);
10268 return gather_children(children, wcroot, local_relpath,
10269 result_pool, scratch_pool);
10274 static svn_error_t *
10275 relocate_txn(svn_wc__db_wcroot_t *wcroot,
10276 const char *local_relpath,
10277 const char *repos_root_url,
10278 const char *repos_uuid,
10279 svn_boolean_t have_base_node,
10280 apr_int64_t old_repos_id,
10281 apr_pool_t *scratch_pool)
10283 svn_sqlite__stmt_t *stmt;
10284 apr_int64_t new_repos_id;
10286 /* This function affects all the children of the given local_relpath,
10287 but the way that it does this is through the repos inheritance mechanism.
10288 So, we only need to rewrite the repos_id of the given local_relpath,
10289 as well as any children with a non-null repos_id, as well as various
10290 repos_id fields in the locks and working_node tables.
10293 /* Get the repos_id for the new repository. */
10294 SVN_ERR(create_repos_id(&new_repos_id, repos_root_url, repos_uuid,
10295 wcroot->sdb, scratch_pool));
10297 /* Set the (base and working) repos_ids and clear the dav_caches */
10298 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10299 STMT_RECURSIVE_UPDATE_NODE_REPO));
10300 SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
10301 old_repos_id, new_repos_id));
10302 SVN_ERR(svn_sqlite__step_done(stmt));
10304 if (have_base_node)
10306 /* Update any locks for the root or its children. */
10307 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10308 STMT_UPDATE_LOCK_REPOS_ID));
10309 SVN_ERR(svn_sqlite__bindf(stmt, "ii", old_repos_id, new_repos_id));
10310 SVN_ERR(svn_sqlite__step_done(stmt));
10313 return SVN_NO_ERROR;
10318 svn_wc__db_global_relocate(svn_wc__db_t *db,
10319 const char *local_dir_abspath,
10320 const char *repos_root_url,
10321 apr_pool_t *scratch_pool)
10323 svn_wc__db_wcroot_t *wcroot;
10324 const char *local_relpath;
10325 const char *local_dir_relpath;
10326 svn_wc__db_status_t status;
10327 const char *repos_uuid;
10328 svn_boolean_t have_base_node;
10329 apr_int64_t old_repos_id;
10331 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
10332 /* ### assert that we were passed a directory? */
10334 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_dir_relpath,
10335 db, local_dir_abspath, scratch_pool, scratch_pool));
10336 VERIFY_USABLE_WCROOT(wcroot);
10337 local_relpath = local_dir_relpath;
10339 SVN_ERR(read_info(&status,
10340 NULL, NULL, NULL, &old_repos_id,
10341 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10342 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10344 &have_base_node, NULL, NULL,
10345 wcroot, local_relpath,
10346 scratch_pool, scratch_pool));
10348 if (status == svn_wc__db_status_excluded)
10350 /* The parent cannot be excluded, so look at the parent and then
10351 adjust the relpath */
10352 const char *parent_relpath = svn_relpath_dirname(local_dir_relpath,
10354 SVN_ERR(read_info(&status,
10355 NULL, NULL, NULL, &old_repos_id,
10356 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10357 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10360 wcroot, parent_relpath,
10361 scratch_pool, scratch_pool));
10362 local_dir_relpath = parent_relpath;
10365 if (old_repos_id == INVALID_REPOS_ID)
10367 /* Do we need to support relocating something that is
10368 added/deleted/excluded without relocating the parent? If not
10369 then perhaps relpath, root_url and uuid should be passed down
10370 to the children so that they don't have to scan? */
10372 if (status == svn_wc__db_status_deleted)
10374 const char *work_del_relpath;
10376 SVN_ERR(scan_deletion_txn(NULL, NULL,
10377 &work_del_relpath, NULL,
10378 wcroot, local_dir_relpath,
10381 if (work_del_relpath)
10383 /* Deleted within a copy/move */
10385 /* The parent of the delete is added. */
10386 status = svn_wc__db_status_added;
10387 local_dir_relpath = svn_relpath_dirname(work_del_relpath,
10392 if (status == svn_wc__db_status_added)
10394 SVN_ERR(scan_addition(NULL, NULL, NULL, &old_repos_id,
10395 NULL, NULL, NULL, NULL, NULL, NULL,
10396 wcroot, local_dir_relpath,
10397 scratch_pool, scratch_pool));
10400 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL,
10402 NULL, NULL, NULL, NULL, NULL,
10403 NULL, NULL, NULL, NULL, NULL,
10404 wcroot, local_dir_relpath,
10405 scratch_pool, scratch_pool));
10408 SVN_ERR(svn_wc__db_fetch_repos_info(NULL, &repos_uuid, wcroot->sdb,
10409 old_repos_id, scratch_pool));
10410 SVN_ERR_ASSERT(repos_uuid);
10412 SVN_WC__DB_WITH_TXN(
10413 relocate_txn(wcroot, local_relpath, repos_root_url, repos_uuid,
10414 have_base_node, old_repos_id, scratch_pool),
10417 return SVN_NO_ERROR;
10421 /* Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of
10422 (WCROOT, LOCAL_RELPATH), directly if its BASE row exists or implied from
10423 its parent's BASE row if not. In the latter case, error if the parent
10424 BASE row does not exist. */
10425 static svn_error_t *
10426 determine_repos_info(apr_int64_t *repos_id,
10427 const char **repos_relpath,
10428 svn_wc__db_wcroot_t *wcroot,
10429 const char *local_relpath,
10430 apr_pool_t *result_pool,
10431 apr_pool_t *scratch_pool)
10433 svn_sqlite__stmt_t *stmt;
10434 svn_boolean_t have_row;
10435 const char *repos_parent_relpath;
10436 const char *local_parent_relpath, *name;
10438 /* ### is it faster to fetch fewer columns? */
10440 /* Prefer the current node's repository information. */
10441 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10442 STMT_SELECT_BASE_NODE));
10443 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10444 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10448 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 0));
10449 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1));
10451 *repos_id = svn_sqlite__column_int64(stmt, 0);
10452 *repos_relpath = svn_sqlite__column_text(stmt, 1, result_pool);
10454 return svn_error_trace(svn_sqlite__reset(stmt));
10457 SVN_ERR(svn_sqlite__reset(stmt));
10459 /* This was a child node within this wcroot. We want to look at the
10460 BASE node of the directory. */
10461 svn_relpath_split(&local_parent_relpath, &name, local_relpath, scratch_pool);
10463 /* The REPOS_ID will be the same (### until we support mixed-repos) */
10464 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
10465 &repos_parent_relpath, repos_id,
10466 NULL, NULL, NULL, NULL, NULL,
10467 NULL, NULL, NULL, NULL, NULL,
10468 wcroot, local_parent_relpath,
10469 scratch_pool, scratch_pool));
10471 *repos_relpath = svn_relpath_join(repos_parent_relpath, name, result_pool);
10473 return SVN_NO_ERROR;
10476 /* Helper for svn_wc__db_global_commit()
10478 Makes local_relpath and all its descendants at the same op-depth represent
10479 the copy origin repos_id:repos_relpath@revision.
10481 This code is only valid to fix-up a move from an old location, to a new
10482 location during a commit.
10485 * local_relpath is not the working copy root (can't be moved)
10486 * repos_relpath is not the repository root (can't be moved)
10488 static svn_error_t *
10489 moved_descendant_commit(svn_wc__db_wcroot_t *wcroot,
10490 const char *local_relpath,
10492 apr_int64_t repos_id,
10493 const char *repos_relpath,
10494 svn_revnum_t revision,
10495 apr_pool_t *scratch_pool)
10497 apr_hash_t *children;
10498 apr_pool_t *iterpool;
10499 svn_sqlite__stmt_t *stmt;
10500 svn_boolean_t have_row;
10501 apr_hash_index_t *hi;
10503 SVN_ERR_ASSERT(*local_relpath != '\0'
10504 && *repos_relpath != '\0');
10506 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10507 STMT_SELECT_MOVED_DESCENDANTS));
10508 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
10512 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10514 return svn_error_trace(svn_sqlite__reset(stmt));
10516 children = apr_hash_make(scratch_pool);
10518 /* First, obtain all moved children */
10519 /* To keep error handling simple, first cache them in a hashtable */
10522 const char *src_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
10523 const char *to_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
10525 svn_hash_sets(children, src_relpath, to_relpath);
10527 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10529 SVN_ERR(svn_sqlite__reset(stmt));
10531 /* Then update them */
10532 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10533 STMT_COMMIT_UPDATE_ORIGIN));
10535 iterpool = svn_pool_create(scratch_pool);
10536 for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
10538 const char *src_relpath = svn__apr_hash_index_key(hi);
10539 const char *to_relpath = svn__apr_hash_index_val(hi);
10540 const char *new_repos_relpath;
10541 int to_op_depth = relpath_depth(to_relpath);
10544 svn_pool_clear(iterpool);
10546 SVN_ERR_ASSERT(to_op_depth > 0);
10548 new_repos_relpath = svn_relpath_join(
10550 svn_relpath_skip_ancestor(local_relpath,
10554 SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
10560 SVN_ERR(svn_sqlite__update(&affected, stmt));
10563 /* Enable in release code?
10564 Broken moves are not fatal yet, but this assertion would break
10566 SVN_ERR_ASSERT(affected >= 1); /* If this fails there is no move dest */
10569 SVN_ERR(moved_descendant_commit(wcroot, to_relpath, to_op_depth,
10570 repos_id, new_repos_relpath, revision,
10574 svn_pool_destroy(iterpool);
10575 return SVN_NO_ERROR;
10578 /* Helper for svn_wc__db_global_commit()
10580 Moves all nodes below LOCAL_RELPATH from op-depth OP_DEPTH to op-depth 0
10581 (BASE), setting their presence to 'not-present' if their presence wasn't
10584 Makes all nodes below LOCAL_RELPATH represent the descendants of repository
10585 location repos_id:repos_relpath@revision.
10588 * local_relpath is not the working copy root (can't be replaced)
10589 * repos_relpath is not the repository root (can't be replaced)
10591 static svn_error_t *
10592 descendant_commit(svn_wc__db_wcroot_t *wcroot,
10593 const char *local_relpath,
10595 apr_int64_t repos_id,
10596 const char *repos_relpath,
10597 svn_revnum_t revision,
10598 apr_pool_t *scratch_pool)
10600 svn_sqlite__stmt_t *stmt;
10602 SVN_ERR_ASSERT(*local_relpath != '\0'
10603 && *repos_relpath != '\0');
10605 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10606 STMT_COMMIT_DESCENDANTS_TO_BASE));
10608 SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
10615 SVN_ERR(svn_sqlite__update(NULL, stmt));
10617 return SVN_NO_ERROR;
10620 /* The body of svn_wc__db_global_commit().
10622 static svn_error_t *
10623 commit_node(svn_wc__db_wcroot_t *wcroot,
10624 const char *local_relpath,
10625 svn_revnum_t new_revision,
10626 svn_revnum_t changed_rev,
10627 apr_time_t changed_date,
10628 const char *changed_author,
10629 const svn_checksum_t *new_checksum,
10630 const apr_array_header_t *new_children,
10631 apr_hash_t *new_dav_cache,
10632 svn_boolean_t keep_changelist,
10633 svn_boolean_t no_unlock,
10634 const svn_skel_t *work_items,
10635 apr_pool_t *scratch_pool)
10637 svn_sqlite__stmt_t *stmt_info;
10638 svn_sqlite__stmt_t *stmt_act;
10639 svn_boolean_t have_act;
10640 svn_string_t prop_blob = { 0 };
10641 svn_string_t inherited_prop_blob = { 0 };
10642 const char *changelist = NULL;
10643 const char *parent_relpath;
10644 svn_wc__db_status_t new_presence;
10645 svn_node_kind_t new_kind;
10646 const char *new_depth_str = NULL;
10647 svn_sqlite__stmt_t *stmt;
10648 apr_int64_t repos_id;
10649 const char *repos_relpath;
10651 svn_wc__db_status_t old_presence;
10653 /* If we are adding a file or directory, then we need to get
10654 repository information from the parent node since "this node" does
10657 For existing nodes, we should retain the (potentially-switched)
10658 repository information. */
10659 SVN_ERR(determine_repos_info(&repos_id, &repos_relpath,
10660 wcroot, local_relpath,
10661 scratch_pool, scratch_pool));
10663 /* ### is it better to select only the data needed? */
10664 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
10665 STMT_SELECT_NODE_INFO));
10666 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
10667 SVN_ERR(svn_sqlite__step_row(stmt_info));
10669 SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
10670 STMT_SELECT_ACTUAL_NODE));
10671 SVN_ERR(svn_sqlite__bindf(stmt_act, "is",
10672 wcroot->wc_id, local_relpath));
10673 SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
10675 /* There should be something to commit! */
10677 op_depth = svn_sqlite__column_int(stmt_info, 0);
10679 /* Figure out the new node's kind. It will be whatever is in WORKING_NODE,
10680 or there will be a BASE_NODE that has it. */
10681 new_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
10683 /* What will the new depth be? */
10684 if (new_kind == svn_node_dir)
10685 new_depth_str = svn_sqlite__column_text(stmt_info, 11, scratch_pool);
10687 /* Check that the repository information is not being changed. */
10690 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 1));
10691 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 2));
10693 /* A commit cannot change these values. */
10694 SVN_ERR_ASSERT(repos_id == svn_sqlite__column_int64(stmt_info, 1));
10695 SVN_ERR_ASSERT(strcmp(repos_relpath,
10696 svn_sqlite__column_text(stmt_info, 2, NULL)) == 0);
10699 /* Find the appropriate new properties -- ACTUAL overrides any properties
10700 in WORKING that arrived as part of a copy/move.
10702 Note: we'll keep them as a big blob of data, rather than
10703 deserialize/serialize them. */
10705 prop_blob.data = svn_sqlite__column_blob(stmt_act, 1, &prop_blob.len,
10707 if (prop_blob.data == NULL)
10708 prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len,
10711 inherited_prop_blob.data = svn_sqlite__column_blob(stmt_info, 16,
10712 &inherited_prop_blob.len,
10715 if (keep_changelist && have_act)
10716 changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool);
10718 old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map);
10720 /* ### other stuff? */
10722 SVN_ERR(svn_sqlite__reset(stmt_info));
10723 SVN_ERR(svn_sqlite__reset(stmt_act));
10729 /* This removes all layers of this node and at the same time determines
10730 if we need to remove shadowed layers below our descendants. */
10732 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10733 STMT_DELETE_ALL_LAYERS));
10734 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10735 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
10737 if (affected_rows > 1)
10739 /* We commit a shadowing operation
10741 1) Remove all shadowed nodes
10742 2) And remove all nodes that have a base-deleted as lowest layer,
10743 because 1) removed that layer */
10745 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10746 STMT_DELETE_SHADOWED_RECURSIVE));
10748 SVN_ERR(svn_sqlite__bindf(stmt,
10754 SVN_ERR(svn_sqlite__step_done(stmt));
10757 /* Note that while these two calls look so similar that they might
10758 be integrated, they really affect a different op-depth and
10759 completely different nodes (via a different recursion pattern). */
10761 /* Collapse descendants of the current op_depth in layer 0 */
10762 SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth,
10763 repos_id, repos_relpath, new_revision,
10766 /* And make the recorded local moves represent moves of the node we just
10768 SVN_ERR(moved_descendant_commit(wcroot, local_relpath, 0,
10769 repos_id, repos_relpath, new_revision,
10772 /* This node is no longer modified, so no node was moved here */
10773 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10774 STMT_CLEAR_MOVED_TO_FROM_DEST));
10775 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
10778 SVN_ERR(svn_sqlite__step_done(stmt));
10781 /* Update or add the BASE_NODE row with all the new information. */
10783 if (*local_relpath == '\0')
10784 parent_relpath = NULL;
10786 parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
10788 /* Preserve any incomplete status */
10789 new_presence = (old_presence == svn_wc__db_status_incomplete
10790 ? svn_wc__db_status_incomplete
10791 : svn_wc__db_status_normal);
10793 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10794 STMT_APPLY_CHANGES_TO_BASE_NODE));
10795 /* symlink_target not yet used */
10796 SVN_ERR(svn_sqlite__bindf(stmt, "issisrtstrisnbn",
10797 wcroot->wc_id, local_relpath,
10802 presence_map, new_presence,
10804 kind_map, new_kind,
10808 prop_blob.data, prop_blob.len));
10810 SVN_ERR(svn_sqlite__bind_checksum(stmt, 13, new_checksum,
10812 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, new_dav_cache,
10814 if (inherited_prop_blob.data != NULL)
10816 SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data,
10817 inherited_prop_blob.len));
10820 SVN_ERR(svn_sqlite__step_done(stmt));
10824 if (keep_changelist && changelist != NULL)
10826 /* The user told us to keep the changelist. Replace the row in
10827 ACTUAL_NODE with the basic keys and the changelist. */
10828 SVN_ERR(svn_sqlite__get_statement(
10829 &stmt, wcroot->sdb,
10830 STMT_RESET_ACTUAL_WITH_CHANGELIST));
10831 SVN_ERR(svn_sqlite__bindf(stmt, "isss",
10832 wcroot->wc_id, local_relpath,
10833 svn_relpath_dirname(local_relpath,
10836 SVN_ERR(svn_sqlite__step_done(stmt));
10840 /* Toss the ACTUAL_NODE row. */
10841 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10842 STMT_DELETE_ACTUAL_NODE));
10843 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10844 SVN_ERR(svn_sqlite__step_done(stmt));
10848 if (new_kind == svn_node_dir)
10850 /* When committing a directory, we should have its new children. */
10851 /* ### one day. just not today. */
10853 SVN_ERR_ASSERT(new_children != NULL);
10856 /* ### process the children */
10861 svn_sqlite__stmt_t *lock_stmt;
10863 SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
10864 STMT_DELETE_LOCK_RECURSIVELY));
10865 SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
10866 SVN_ERR(svn_sqlite__step_done(lock_stmt));
10869 /* Install any work items into the queue, as part of this transaction. */
10870 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
10872 return SVN_NO_ERROR;
10877 svn_wc__db_global_commit(svn_wc__db_t *db,
10878 const char *local_abspath,
10879 svn_revnum_t new_revision,
10880 svn_revnum_t changed_revision,
10881 apr_time_t changed_date,
10882 const char *changed_author,
10883 const svn_checksum_t *new_checksum,
10884 const apr_array_header_t *new_children,
10885 apr_hash_t *new_dav_cache,
10886 svn_boolean_t keep_changelist,
10887 svn_boolean_t no_unlock,
10888 const svn_skel_t *work_items,
10889 apr_pool_t *scratch_pool)
10891 const char *local_relpath;
10892 svn_wc__db_wcroot_t *wcroot;
10894 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10895 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
10896 SVN_ERR_ASSERT(new_checksum == NULL || new_children == NULL);
10898 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10899 local_abspath, scratch_pool, scratch_pool));
10900 VERIFY_USABLE_WCROOT(wcroot);
10902 SVN_WC__DB_WITH_TXN(
10903 commit_node(wcroot, local_relpath,
10904 new_revision, changed_revision, changed_date, changed_author,
10905 new_checksum, new_children, new_dav_cache, keep_changelist,
10906 no_unlock, work_items, scratch_pool),
10909 /* We *totally* monkeyed the entries. Toss 'em. */
10910 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
10912 return SVN_NO_ERROR;
10917 svn_wc__db_global_update(svn_wc__db_t *db,
10918 const char *local_abspath,
10919 svn_node_kind_t new_kind,
10920 const char *new_repos_relpath,
10921 svn_revnum_t new_revision,
10922 const apr_hash_t *new_props,
10923 svn_revnum_t new_changed_rev,
10924 apr_time_t new_changed_date,
10925 const char *new_changed_author,
10926 const apr_array_header_t *new_children,
10927 const svn_checksum_t *new_checksum,
10928 const char *new_target,
10929 const apr_hash_t *new_dav_cache,
10930 const svn_skel_t *conflict,
10931 const svn_skel_t *work_items,
10932 apr_pool_t *scratch_pool)
10937 svn_wc__db_wcroot_t *wcroot;
10938 const char *local_relpath;
10940 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10941 /* ### allow NULL for NEW_REPOS_RELPATH to indicate "no change"? */
10942 SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
10943 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
10944 SVN_ERR_ASSERT(new_props != NULL);
10945 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_changed_rev));
10946 SVN_ERR_ASSERT((new_children != NULL
10947 && new_checksum == NULL
10948 && new_target == NULL)
10949 || (new_children == NULL
10950 && new_checksum != NULL
10951 && new_target == NULL)
10952 || (new_children == NULL
10953 && new_checksum == NULL
10954 && new_target != NULL));
10956 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10957 local_abspath, scratch_pool, scratch_pool));
10958 VERIFY_USABLE_WCROOT(wcroot);
10960 SVN_WC__DB_WITH_TXN(
10961 update_node(wcroot, local_relpath,
10962 new_repos_relpath, new_revision, new_props,
10963 new_changed_rev, new_changed_date, new_changed_author,
10964 new_children, new_checksum, new_target,
10965 conflict, work_items, scratch_pool),
10968 /* We *totally* monkeyed the entries. Toss 'em. */
10969 SVN_ERR(flush_entries(wcroot, local_abspath, scratch_pool));
10971 return SVN_NO_ERROR;
10975 /* Sets a base nodes revision, repository relative path, and/or inherited
10976 propertis. If LOCAL_ABSPATH's rev (REV) is valid, set its revision. If
10977 SET_REPOS_RELPATH is TRUE set its repository relative path to REPOS_RELPATH
10978 (and make sure its REPOS_ID is still valid). If IPROPS is not NULL set its
10979 inherited properties to IPROPS, if IPROPS is NULL then clear any the iprops
10980 cache for the base node.
10982 static svn_error_t *
10983 db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot,
10984 const char *local_relpath,
10985 apr_array_header_t *iprops,
10987 svn_boolean_t set_repos_relpath,
10988 const char *repos_relpath,
10989 apr_int64_t repos_id,
10990 apr_pool_t *scratch_pool)
10992 svn_sqlite__stmt_t *stmt;
10994 SVN_ERR(flush_entries(wcroot,
10995 svn_dirent_join(wcroot->abspath, local_relpath,
10997 svn_depth_empty, scratch_pool));
11000 if (SVN_IS_VALID_REVNUM(rev))
11002 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11003 STMT_UPDATE_BASE_REVISION));
11005 SVN_ERR(svn_sqlite__bindf(stmt, "isr", wcroot->wc_id, local_relpath,
11008 SVN_ERR(svn_sqlite__step_done(stmt));
11011 if (set_repos_relpath)
11013 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11014 STMT_UPDATE_BASE_REPOS));
11016 SVN_ERR(svn_sqlite__bindf(stmt, "isis", wcroot->wc_id, local_relpath,
11017 repos_id, repos_relpath));
11019 SVN_ERR(svn_sqlite__step_done(stmt));
11022 /* Set or clear iprops. */
11023 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11024 STMT_UPDATE_IPROP));
11025 SVN_ERR(svn_sqlite__bindf(stmt, "is",
11028 SVN_ERR(svn_sqlite__bind_iprops(stmt, 3, iprops, scratch_pool));
11029 SVN_ERR(svn_sqlite__step_done(stmt));
11031 return SVN_NO_ERROR;
11034 /* The main body of bump_revisions_post_update().
11036 * Tweak the information for LOCAL_RELPATH in WCROOT. If NEW_REPOS_RELPATH is
11037 * non-NULL update the entry to the new url specified by NEW_REPOS_RELPATH,
11038 * NEW_REPOS_ID. If NEW_REV is valid, make this the node's working revision.
11040 * If WCROOT_IPROPS is not NULL it is a hash mapping const char * absolute
11041 * working copy paths to depth-first ordered arrays of
11042 * svn_prop_inherited_item_t * structures. If the absolute path equivalent
11043 * of LOCAL_RELPATH exists in WCROOT_IPROPS, then set the hashed value as the
11044 * node's inherited properties.
11046 * Unless S_ROOT is TRUE the tweaks might cause the node for LOCAL_ABSPATH to
11047 * be removed from the WC; if IS_ROOT is TRUE this will not happen.
11049 static svn_error_t *
11050 bump_node_revision(svn_wc__db_wcroot_t *wcroot,
11051 const char *local_relpath,
11052 apr_int64_t new_repos_id,
11053 const char *new_repos_relpath,
11054 svn_revnum_t new_rev,
11056 apr_hash_t *exclude_relpaths,
11057 apr_hash_t *wcroot_iprops,
11058 svn_boolean_t is_root,
11059 svn_boolean_t skip_when_dir,
11061 apr_pool_t *scratch_pool)
11063 apr_pool_t *iterpool;
11064 const apr_array_header_t *children;
11066 svn_wc__db_status_t status;
11067 svn_node_kind_t db_kind;
11068 svn_revnum_t revision;
11069 const char *repos_relpath;
11070 apr_int64_t repos_id;
11071 svn_boolean_t set_repos_relpath = FALSE;
11072 svn_boolean_t update_root;
11073 svn_depth_t depth_below_here = depth;
11074 apr_array_header_t *iprops = NULL;
11076 /* Skip an excluded path and its descendants. */
11077 if (svn_hash_gets(exclude_relpaths, local_relpath))
11078 return SVN_NO_ERROR;
11080 SVN_ERR(svn_wc__db_base_get_info_internal(&status, &db_kind, &revision,
11081 &repos_relpath, &repos_id,
11082 NULL, NULL, NULL, NULL, NULL,
11083 NULL, NULL, NULL, NULL, &update_root,
11084 wcroot, local_relpath,
11085 scratch_pool, scratch_pool));
11087 /* Skip file externals */
11089 && db_kind == svn_node_file
11091 return SVN_NO_ERROR;
11093 if (skip_when_dir && db_kind == svn_node_dir)
11094 return SVN_NO_ERROR;
11096 /* If the node is still marked 'not-present', then the server did not
11097 re-add it. So it's really gone in this revision, thus we remove the node.
11099 If the node is still marked 'server-excluded' and yet is not the same
11100 revision as new_rev, then the server did not re-add it, nor
11101 re-server-exclude it, so we can remove the node. */
11103 && (status == svn_wc__db_status_not_present
11104 || (status == svn_wc__db_status_server_excluded &&
11105 revision != new_rev)))
11107 return svn_error_trace(db_base_remove(wcroot, local_relpath,
11108 db, FALSE, FALSE, FALSE,
11109 SVN_INVALID_REVNUM,
11110 NULL, NULL, scratch_pool));
11113 if (new_repos_relpath != NULL && strcmp(repos_relpath, new_repos_relpath))
11114 set_repos_relpath = TRUE;
11117 iprops = svn_hash_gets(wcroot_iprops,
11118 svn_dirent_join(wcroot->abspath, local_relpath,
11122 || set_repos_relpath
11123 || (SVN_IS_VALID_REVNUM(new_rev) && new_rev != revision))
11125 SVN_ERR(db_op_set_rev_repos_relpath_iprops(wcroot, local_relpath,
11134 if (depth <= svn_depth_empty
11135 || db_kind != svn_node_dir
11136 || status == svn_wc__db_status_server_excluded
11137 || status == svn_wc__db_status_excluded
11138 || status == svn_wc__db_status_not_present)
11139 return SVN_NO_ERROR;
11141 /* And now recurse over the children */
11143 depth_below_here = depth;
11145 if (depth == svn_depth_immediates || depth == svn_depth_files)
11146 depth_below_here = svn_depth_empty;
11148 iterpool = svn_pool_create(scratch_pool);
11150 SVN_ERR(gather_repo_children(&children, wcroot, local_relpath, 0,
11151 scratch_pool, iterpool));
11152 for (i = 0; i < children->nelts; i++)
11154 const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
11155 const char *child_local_relpath;
11156 const char *child_repos_relpath = NULL;
11158 svn_pool_clear(iterpool);
11160 /* Derive the new URL for the current (child) entry */
11161 if (new_repos_relpath)
11162 child_repos_relpath = svn_relpath_join(new_repos_relpath,
11163 child_basename, iterpool);
11165 child_local_relpath = svn_relpath_join(local_relpath, child_basename,
11168 SVN_ERR(bump_node_revision(wcroot, child_local_relpath, new_repos_id,
11169 child_repos_relpath, new_rev,
11171 exclude_relpaths, wcroot_iprops,
11172 FALSE /* is_root */,
11173 (depth < svn_depth_immediates), db,
11178 svn_pool_destroy(iterpool);
11180 return SVN_NO_ERROR;
11183 /* Helper for svn_wc__db_op_bump_revisions_post_update().
11185 static svn_error_t *
11186 bump_revisions_post_update(svn_wc__db_wcroot_t *wcroot,
11187 const char *local_relpath,
11190 const char *new_repos_relpath,
11191 const char *new_repos_root_url,
11192 const char *new_repos_uuid,
11193 svn_revnum_t new_revision,
11194 apr_hash_t *exclude_relpaths,
11195 apr_hash_t *wcroot_iprops,
11196 svn_wc_notify_func2_t notify_func,
11197 void *notify_baton,
11198 apr_pool_t *scratch_pool)
11200 svn_wc__db_status_t status;
11201 svn_node_kind_t kind;
11203 apr_int64_t new_repos_id = INVALID_REPOS_ID;
11205 err = svn_wc__db_base_get_info_internal(&status, &kind, NULL, NULL, NULL,
11206 NULL, NULL, NULL, NULL, NULL, NULL,
11207 NULL, NULL, NULL, NULL,
11208 wcroot, local_relpath,
11209 scratch_pool, scratch_pool);
11210 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
11212 svn_error_clear(err);
11213 return SVN_NO_ERROR;
11220 case svn_wc__db_status_excluded:
11221 case svn_wc__db_status_server_excluded:
11222 case svn_wc__db_status_not_present:
11223 return SVN_NO_ERROR;
11225 /* Explicitly ignore other statii */
11230 if (new_repos_root_url != NULL)
11231 SVN_ERR(create_repos_id(&new_repos_id, new_repos_root_url,
11233 wcroot->sdb, scratch_pool));
11235 SVN_ERR(bump_node_revision(wcroot, local_relpath, new_repos_id,
11236 new_repos_relpath, new_revision,
11237 depth, exclude_relpaths,
11239 TRUE /* is_root */, FALSE, db,
11242 SVN_ERR(svn_wc__db_bump_moved_away(wcroot, local_relpath, depth, db,
11245 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, SVN_INVALID_REVNUM,
11246 SVN_INVALID_REVNUM, notify_func,
11247 notify_baton, scratch_pool));
11249 return SVN_NO_ERROR;
11253 svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db,
11254 const char *local_abspath,
11256 const char *new_repos_relpath,
11257 const char *new_repos_root_url,
11258 const char *new_repos_uuid,
11259 svn_revnum_t new_revision,
11260 apr_hash_t *exclude_relpaths,
11261 apr_hash_t *wcroot_iprops,
11262 svn_wc_notify_func2_t notify_func,
11263 void *notify_baton,
11264 apr_pool_t *scratch_pool)
11266 const char *local_relpath;
11267 svn_wc__db_wcroot_t *wcroot;
11269 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11270 local_abspath, scratch_pool, scratch_pool));
11272 VERIFY_USABLE_WCROOT(wcroot);
11274 if (svn_hash_gets(exclude_relpaths, local_relpath))
11275 return SVN_NO_ERROR;
11277 if (depth == svn_depth_unknown)
11278 depth = svn_depth_infinity;
11280 SVN_WC__DB_WITH_TXN(
11281 bump_revisions_post_update(wcroot, local_relpath, db,
11282 depth, new_repos_relpath, new_repos_root_url,
11283 new_repos_uuid, new_revision,
11284 exclude_relpaths, wcroot_iprops,
11285 notify_func, notify_baton, scratch_pool),
11288 return SVN_NO_ERROR;
11291 /* The body of svn_wc__db_lock_add().
11293 static svn_error_t *
11294 lock_add_txn(svn_wc__db_wcroot_t *wcroot,
11295 const char *local_relpath,
11296 const svn_wc__db_lock_t *lock,
11297 apr_pool_t *scratch_pool)
11299 svn_sqlite__stmt_t *stmt;
11300 const char *repos_relpath;
11301 apr_int64_t repos_id;
11303 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11304 &repos_relpath, &repos_id,
11305 NULL, NULL, NULL, NULL, NULL,
11306 NULL, NULL, NULL, NULL, NULL,
11307 wcroot, local_relpath,
11308 scratch_pool, scratch_pool));
11310 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_LOCK));
11311 SVN_ERR(svn_sqlite__bindf(stmt, "iss",
11312 repos_id, repos_relpath, lock->token));
11314 if (lock->owner != NULL)
11315 SVN_ERR(svn_sqlite__bind_text(stmt, 4, lock->owner));
11317 if (lock->comment != NULL)
11318 SVN_ERR(svn_sqlite__bind_text(stmt, 5, lock->comment));
11320 if (lock->date != 0)
11321 SVN_ERR(svn_sqlite__bind_int64(stmt, 6, lock->date));
11323 SVN_ERR(svn_sqlite__insert(NULL, stmt));
11325 return SVN_NO_ERROR;
11330 svn_wc__db_lock_add(svn_wc__db_t *db,
11331 const char *local_abspath,
11332 const svn_wc__db_lock_t *lock,
11333 apr_pool_t *scratch_pool)
11335 svn_wc__db_wcroot_t *wcroot;
11336 const char *local_relpath;
11338 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11339 SVN_ERR_ASSERT(lock != NULL);
11341 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11342 local_abspath, scratch_pool, scratch_pool));
11343 VERIFY_USABLE_WCROOT(wcroot);
11345 SVN_WC__DB_WITH_TXN(
11346 lock_add_txn(wcroot, local_relpath, lock, scratch_pool),
11349 /* There may be some entries, and the lock info is now out of date. */
11350 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
11352 return SVN_NO_ERROR;
11356 /* The body of svn_wc__db_lock_remove().
11358 static svn_error_t *
11359 lock_remove_txn(svn_wc__db_wcroot_t *wcroot,
11360 const char *local_relpath,
11361 apr_pool_t *scratch_pool)
11363 const char *repos_relpath;
11364 apr_int64_t repos_id;
11365 svn_sqlite__stmt_t *stmt;
11367 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11368 &repos_relpath, &repos_id,
11369 NULL, NULL, NULL, NULL, NULL,
11370 NULL, NULL, NULL, NULL, NULL,
11371 wcroot, local_relpath,
11372 scratch_pool, scratch_pool));
11374 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11375 STMT_DELETE_LOCK));
11376 SVN_ERR(svn_sqlite__bindf(stmt, "is", repos_id, repos_relpath));
11378 SVN_ERR(svn_sqlite__step_done(stmt));
11380 return SVN_NO_ERROR;
11385 svn_wc__db_lock_remove(svn_wc__db_t *db,
11386 const char *local_abspath,
11387 apr_pool_t *scratch_pool)
11389 svn_wc__db_wcroot_t *wcroot;
11390 const char *local_relpath;
11392 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11394 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11395 local_abspath, scratch_pool, scratch_pool));
11396 VERIFY_USABLE_WCROOT(wcroot);
11398 SVN_WC__DB_WITH_TXN(
11399 lock_remove_txn(wcroot, local_relpath, scratch_pool),
11402 /* There may be some entries, and the lock info is now out of date. */
11403 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
11405 return SVN_NO_ERROR;
11410 svn_wc__db_scan_base_repos(const char **repos_relpath,
11411 const char **repos_root_url,
11412 const char **repos_uuid,
11414 const char *local_abspath,
11415 apr_pool_t *result_pool,
11416 apr_pool_t *scratch_pool)
11418 svn_wc__db_wcroot_t *wcroot;
11419 const char *local_relpath;
11420 apr_int64_t repos_id;
11422 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11424 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11425 local_abspath, scratch_pool, scratch_pool));
11426 VERIFY_USABLE_WCROOT(wcroot);
11428 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11429 repos_relpath, &repos_id,
11430 NULL, NULL, NULL, NULL, NULL,
11431 NULL, NULL, NULL, NULL, NULL,
11432 wcroot, local_relpath,
11433 result_pool, scratch_pool));
11434 SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot->sdb,
11435 repos_id, result_pool));
11437 return SVN_NO_ERROR;
11441 /* A helper for scan_addition().
11442 * Compute moved-from information for the node at LOCAL_RELPATH which
11443 * has been determined as having been moved-here.
11444 * If MOVED_FROM_RELPATH is not NULL, set *MOVED_FROM_RELPATH to the
11445 * path of the move-source node in *MOVED_FROM_RELPATH.
11446 * If DELETE_OP_ROOT_RELPATH is not NULL, set *DELETE_OP_ROOT_RELPATH
11447 * to the path of the op-root of the delete-half of the move.
11448 * If moved-from information cannot be derived, set both *MOVED_FROM_RELPATH
11449 * and *DELETE_OP_ROOT_RELPATH to NULL, and return a "copied" status.
11450 * COPY_OPT_ROOT_RELPATH is the relpath of the op-root of the copied-half
11452 static svn_error_t *
11453 get_moved_from_info(const char **moved_from_relpath,
11454 const char **moved_from_op_root_relpath,
11455 const char *moved_to_op_root_relpath,
11457 svn_wc__db_wcroot_t *wcroot,
11458 const char *local_relpath,
11459 apr_pool_t *result_pool,
11460 apr_pool_t *scratch_pool)
11462 svn_sqlite__stmt_t *stmt;
11463 svn_boolean_t have_row;
11465 /* Run a query to get the moved-from path from the DB. */
11466 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11467 STMT_SELECT_MOVED_FROM_RELPATH));
11468 SVN_ERR(svn_sqlite__bindf(stmt, "is",
11469 wcroot->wc_id, moved_to_op_root_relpath));
11470 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11474 /* The move was only recorded at the copy-half, possibly because
11475 * the move operation was interrupted mid-way between the copy
11476 * and the delete. Treat this node as a normal copy. */
11477 if (moved_from_relpath)
11478 *moved_from_relpath = NULL;
11479 if (moved_from_op_root_relpath)
11480 *moved_from_op_root_relpath = NULL;
11482 SVN_ERR(svn_sqlite__reset(stmt));
11483 return SVN_NO_ERROR;
11487 *op_depth = svn_sqlite__column_int(stmt, 1);
11489 if (moved_from_relpath || moved_from_op_root_relpath)
11491 const char *db_delete_op_root_relpath;
11493 /* The moved-from path from the DB is the relpath of
11494 * the op_root of the delete-half of the move. */
11495 db_delete_op_root_relpath = svn_sqlite__column_text(stmt, 0,
11497 if (moved_from_op_root_relpath)
11498 *moved_from_op_root_relpath = db_delete_op_root_relpath;
11500 if (moved_from_relpath)
11502 if (strcmp(moved_to_op_root_relpath, local_relpath) == 0)
11504 /* LOCAL_RELPATH is the op_root of the copied-half of the
11505 * move, so the correct MOVED_FROM_ABSPATH is the op-root
11506 * of the delete-half. */
11507 *moved_from_relpath = db_delete_op_root_relpath;
11511 const char *child_relpath;
11513 /* LOCAL_RELPATH is a child that was copied along with the
11514 * op_root of the copied-half of the move. Construct the
11515 * corresponding path beneath the op_root of the delete-half. */
11517 /* Grab the child path relative to the op_root of the move
11519 child_relpath = svn_relpath_skip_ancestor(
11520 moved_to_op_root_relpath, local_relpath);
11522 SVN_ERR_ASSERT(child_relpath && strlen(child_relpath) > 0);
11524 /* This join is valid because LOCAL_RELPATH has not been moved
11525 * within the copied-half of the move yet -- else, it would
11526 * be its own op_root. */
11527 *moved_from_relpath = svn_relpath_join(db_delete_op_root_relpath,
11534 SVN_ERR(svn_sqlite__reset(stmt));
11536 return SVN_NO_ERROR;
11539 /* The body of scan_addition().
11541 static svn_error_t *
11542 scan_addition_txn(svn_wc__db_status_t *status,
11543 const char **op_root_relpath_p,
11544 const char **repos_relpath,
11545 apr_int64_t *repos_id,
11546 const char **original_repos_relpath,
11547 apr_int64_t *original_repos_id,
11548 svn_revnum_t *original_revision,
11549 const char **moved_from_relpath,
11550 const char **moved_from_op_root_relpath,
11551 int *moved_from_op_depth,
11552 svn_wc__db_wcroot_t *wcroot,
11553 const char *local_relpath,
11554 apr_pool_t *result_pool,
11555 apr_pool_t *scratch_pool)
11557 const char *op_root_relpath;
11558 const char *build_relpath = "";
11560 /* Initialize most of the OUT parameters. Generally, we'll only be filling
11561 in a subset of these, so it is easier to init all up front. Note that
11562 the STATUS parameter will be initialized once we read the status of
11563 the specified node. */
11564 if (op_root_relpath_p)
11565 *op_root_relpath_p = NULL;
11566 if (original_repos_relpath)
11567 *original_repos_relpath = NULL;
11568 if (original_repos_id)
11569 *original_repos_id = INVALID_REPOS_ID;
11570 if (original_revision)
11571 *original_revision = SVN_INVALID_REVNUM;
11572 if (moved_from_relpath)
11573 *moved_from_relpath = NULL;
11574 if (moved_from_op_root_relpath)
11575 *moved_from_op_root_relpath = NULL;
11576 if (moved_from_op_depth)
11577 *moved_from_op_depth = 0;
11580 svn_sqlite__stmt_t *stmt;
11581 svn_boolean_t have_row;
11582 svn_wc__db_status_t presence;
11584 const char *repos_prefix_path = "";
11587 /* ### is it faster to fetch fewer columns? */
11588 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11589 STMT_SELECT_WORKING_NODE));
11590 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11591 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11595 /* Reset statement before returning */
11596 SVN_ERR(svn_sqlite__reset(stmt));
11598 /* ### maybe we should return a usage error instead? */
11599 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
11600 _("The node '%s' was not found."),
11601 path_for_error_message(wcroot,
11606 presence = svn_sqlite__column_token(stmt, 1, presence_map);
11608 /* The starting node should exist normally. */
11609 op_depth = svn_sqlite__column_int(stmt, 0);
11610 if (op_depth == 0 || (presence != svn_wc__db_status_normal
11611 && presence != svn_wc__db_status_incomplete))
11612 /* reset the statement as part of the error generation process */
11613 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
11614 svn_sqlite__reset(stmt),
11615 _("Expected node '%s' to be added."),
11616 path_for_error_message(wcroot,
11620 if (original_revision)
11621 *original_revision = svn_sqlite__column_revnum(stmt, 12);
11623 /* Provide the default status; we'll override as appropriate. */
11626 if (presence == svn_wc__db_status_normal)
11627 *status = svn_wc__db_status_added;
11629 *status = svn_wc__db_status_incomplete;
11633 /* Calculate the op root local path components */
11634 op_root_relpath = local_relpath;
11636 for (i = relpath_depth(local_relpath); i > op_depth; --i)
11638 /* Calculate the path of the operation root */
11639 repos_prefix_path =
11640 svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
11643 op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
11646 if (op_root_relpath_p)
11647 *op_root_relpath_p = apr_pstrdup(result_pool, op_root_relpath);
11649 /* ### This if-statement is quite redundant.
11650 * ### We're checking all these values again within the body anyway.
11651 * ### The body should be broken up appropriately and move into the
11652 * ### outer scope. */
11653 if (original_repos_relpath
11654 || original_repos_id
11655 || (original_revision
11656 && *original_revision == SVN_INVALID_REVNUM)
11658 || moved_from_relpath || moved_from_op_root_relpath)
11660 if (local_relpath != op_root_relpath)
11661 /* requery to get the add/copy root */
11663 SVN_ERR(svn_sqlite__reset(stmt));
11665 SVN_ERR(svn_sqlite__bindf(stmt, "is",
11666 wcroot->wc_id, op_root_relpath));
11667 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11671 /* Reset statement before returning */
11672 SVN_ERR(svn_sqlite__reset(stmt));
11674 /* ### maybe we should return a usage error instead? */
11675 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
11676 _("The node '%s' was not found."),
11677 path_for_error_message(wcroot,
11682 if (original_revision
11683 && *original_revision == SVN_INVALID_REVNUM)
11684 *original_revision = svn_sqlite__column_revnum(stmt, 12);
11687 if (original_repos_relpath)
11688 *original_repos_relpath = svn_sqlite__column_text(stmt, 11,
11691 if (!svn_sqlite__column_is_null(stmt, 10)
11693 || original_repos_id
11694 || moved_from_relpath || moved_from_op_root_relpath))
11695 /* If column 10 (original_repos_id) is NULL,
11696 this is a plain add, not a copy or a move */
11698 svn_boolean_t moved_here;
11699 if (original_repos_id)
11700 *original_repos_id = svn_sqlite__column_int64(stmt, 10);
11702 moved_here = svn_sqlite__column_boolean(stmt, 13 /* moved_here */);
11704 *status = moved_here ? svn_wc__db_status_moved_here
11705 : svn_wc__db_status_copied;
11708 && (moved_from_relpath || moved_from_op_root_relpath))
11712 err = get_moved_from_info(moved_from_relpath,
11713 moved_from_op_root_relpath,
11715 moved_from_op_depth,
11716 wcroot, local_relpath,
11721 return svn_error_compose_create(
11722 err, svn_sqlite__reset(stmt));
11728 /* ### This loop here is to skip up to the first node which is a BASE node,
11729 because base_get_info() doesn't accommodate the scenario that
11730 we're looking at here; we found the true op_root, which may be inside
11731 further changed trees. */
11732 if (repos_relpath || repos_id)
11734 const char *base_relpath;
11739 SVN_ERR(svn_sqlite__reset(stmt));
11741 /* Pointing at op_depth, look at the parent */
11742 repos_prefix_path =
11743 svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
11746 op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
11749 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, op_root_relpath));
11750 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11755 op_depth = svn_sqlite__column_int(stmt, 0);
11757 /* Skip to op_depth */
11758 for (i = relpath_depth(op_root_relpath); i > op_depth; i--)
11760 /* Calculate the path of the operation root */
11761 repos_prefix_path =
11762 svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
11766 svn_relpath_dirname(op_root_relpath, scratch_pool);
11770 SVN_ERR(svn_sqlite__reset(stmt));
11772 build_relpath = repos_prefix_path;
11774 /* If we're here, then we have an added/copied/moved (start) node, and
11775 CURRENT_ABSPATH now points to a BASE node. Figure out the repository
11776 information for the current node, and use that to compute the start
11777 node's repository information. */
11778 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11779 &base_relpath, repos_id,
11780 NULL, NULL, NULL, NULL, NULL,
11781 NULL, NULL, NULL, NULL, NULL,
11782 wcroot, op_root_relpath,
11783 scratch_pool, scratch_pool));
11786 *repos_relpath = svn_relpath_join(base_relpath, build_relpath,
11790 SVN_ERR(svn_sqlite__reset(stmt));
11792 /* Postconditions */
11796 SVN_ERR_ASSERT(*status == svn_wc__db_status_added
11797 || *status == svn_wc__db_status_copied
11798 || *status == svn_wc__db_status_incomplete
11799 || *status == svn_wc__db_status_moved_here);
11800 if (*status == svn_wc__db_status_added)
11802 SVN_ERR_ASSERT(!original_repos_relpath
11803 || *original_repos_relpath == NULL);
11804 SVN_ERR_ASSERT(!original_revision
11805 || *original_revision == SVN_INVALID_REVNUM);
11806 SVN_ERR_ASSERT(!original_repos_id
11807 || *original_repos_id == INVALID_REPOS_ID);
11809 /* An upgrade with a missing directory can leave INCOMPLETE working
11810 op-roots. See upgrade_tests.py 29: upgrade with missing replaced dir
11812 else if (*status != svn_wc__db_status_incomplete)
11814 SVN_ERR_ASSERT(!original_repos_relpath
11815 || *original_repos_relpath != NULL);
11816 SVN_ERR_ASSERT(!original_revision
11817 || *original_revision != SVN_INVALID_REVNUM);
11818 SVN_ERR_ASSERT(!original_repos_id
11819 || *original_repos_id != INVALID_REPOS_ID);
11822 SVN_ERR_ASSERT(!op_root_relpath_p || *op_root_relpath_p != NULL);
11825 return SVN_NO_ERROR;
11829 /* Like svn_wc__db_scan_addition(), but with WCROOT+LOCAL_RELPATH instead of
11832 The output value of *ORIGINAL_REPOS_ID will be INVALID_REPOS_ID if there
11833 is no 'copy-from' repository. */
11834 static svn_error_t *
11835 scan_addition(svn_wc__db_status_t *status,
11836 const char **op_root_relpath,
11837 const char **repos_relpath,
11838 apr_int64_t *repos_id,
11839 const char **original_repos_relpath,
11840 apr_int64_t *original_repos_id,
11841 svn_revnum_t *original_revision,
11842 const char **moved_from_relpath,
11843 const char **moved_from_op_root_relpath,
11844 int *moved_from_op_depth,
11845 svn_wc__db_wcroot_t *wcroot,
11846 const char *local_relpath,
11847 apr_pool_t *result_pool,
11848 apr_pool_t *scratch_pool)
11850 SVN_WC__DB_WITH_TXN(
11851 scan_addition_txn(status, op_root_relpath, repos_relpath, repos_id,
11852 original_repos_relpath, original_repos_id,
11853 original_revision, moved_from_relpath,
11854 moved_from_op_root_relpath, moved_from_op_depth,
11855 wcroot, local_relpath, result_pool, scratch_pool),
11857 return SVN_NO_ERROR;
11862 svn_wc__db_scan_addition(svn_wc__db_status_t *status,
11863 const char **op_root_abspath,
11864 const char **repos_relpath,
11865 const char **repos_root_url,
11866 const char **repos_uuid,
11867 const char **original_repos_relpath,
11868 const char **original_root_url,
11869 const char **original_uuid,
11870 svn_revnum_t *original_revision,
11872 const char *local_abspath,
11873 apr_pool_t *result_pool,
11874 apr_pool_t *scratch_pool)
11876 svn_wc__db_wcroot_t *wcroot;
11877 const char *local_relpath;
11878 const char *op_root_relpath = NULL;
11879 apr_int64_t repos_id = INVALID_REPOS_ID;
11880 apr_int64_t original_repos_id = INVALID_REPOS_ID;
11881 apr_int64_t *repos_id_p
11882 = (repos_root_url || repos_uuid) ? &repos_id : NULL;
11883 apr_int64_t *original_repos_id_p
11884 = (original_root_url || original_uuid) ? &original_repos_id : NULL;
11886 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11888 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11889 local_abspath, scratch_pool, scratch_pool));
11890 VERIFY_USABLE_WCROOT(wcroot);
11892 SVN_ERR(scan_addition(status,
11896 repos_relpath, repos_id_p,
11897 original_repos_relpath, original_repos_id_p,
11900 wcroot, local_relpath, result_pool, scratch_pool));
11902 if (op_root_abspath)
11903 *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
11905 /* REPOS_ID must be valid if requested; ORIGINAL_REPOS_ID need not be. */
11906 SVN_ERR_ASSERT(repos_id_p == NULL || repos_id != INVALID_REPOS_ID);
11908 SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot->sdb,
11909 repos_id, result_pool));
11910 SVN_ERR(svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
11911 wcroot->sdb, original_repos_id,
11914 return SVN_NO_ERROR;
11918 svn_wc__db_scan_moved(const char **moved_from_abspath,
11919 const char **op_root_abspath,
11920 const char **op_root_moved_from_abspath,
11921 const char **moved_from_delete_abspath,
11923 const char *local_abspath,
11924 apr_pool_t *result_pool,
11925 apr_pool_t *scratch_pool)
11927 svn_wc__db_wcroot_t *wcroot;
11928 const char *local_relpath;
11929 svn_wc__db_status_t status;
11930 const char *op_root_relpath = NULL;
11931 const char *moved_from_relpath = NULL;
11932 const char *moved_from_op_root_relpath = NULL;
11933 int moved_from_op_depth = -1;
11935 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11937 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11938 local_abspath, scratch_pool, scratch_pool));
11939 VERIFY_USABLE_WCROOT(wcroot);
11941 SVN_ERR(scan_addition(&status,
11948 ? &moved_from_relpath
11950 (op_root_moved_from_abspath
11951 || moved_from_delete_abspath)
11952 ? &moved_from_op_root_relpath
11954 moved_from_delete_abspath
11955 ? &moved_from_op_depth
11957 wcroot, local_relpath, scratch_pool, scratch_pool));
11959 if (status != svn_wc__db_status_moved_here || !moved_from_relpath)
11960 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
11961 _("Path '%s' was not moved here"),
11962 path_for_error_message(wcroot, local_relpath,
11965 if (op_root_abspath)
11966 *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
11969 if (moved_from_abspath)
11970 *moved_from_abspath = svn_dirent_join(wcroot->abspath, moved_from_relpath,
11973 if (op_root_moved_from_abspath)
11974 *op_root_moved_from_abspath = svn_dirent_join(wcroot->abspath,
11975 moved_from_op_root_relpath,
11978 /* The deleted node is either where we moved from, or one of its ancestors */
11979 if (moved_from_delete_abspath)
11981 const char *tmp = moved_from_op_root_relpath;
11983 SVN_ERR_ASSERT(moved_from_op_depth >= 0);
11985 while (relpath_depth(tmp) > moved_from_op_depth)
11986 tmp = svn_relpath_dirname(tmp, scratch_pool);
11988 *moved_from_delete_abspath = svn_dirent_join(wcroot->abspath, tmp,
11992 return SVN_NO_ERROR;
11997 static svn_error_t *
11998 follow_moved_to(apr_array_header_t **moved_tos,
12000 const char *repos_path,
12001 svn_revnum_t revision,
12002 svn_wc__db_wcroot_t *wcroot,
12003 const char *local_relpath,
12004 apr_pool_t *result_pool,
12005 apr_pool_t *scratch_pool)
12007 svn_sqlite__stmt_t *stmt;
12008 svn_boolean_t have_row;
12009 int working_op_depth;
12010 const char *ancestor_relpath, *node_moved_to = NULL;
12013 SVN_ERR_ASSERT((!op_depth && !repos_path) || (op_depth && repos_path));
12015 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12016 STMT_SELECT_OP_DEPTH_MOVED_TO));
12017 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
12019 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12022 working_op_depth = svn_sqlite__column_int(stmt, 0);
12023 node_moved_to = svn_sqlite__column_text(stmt, 1, result_pool);
12026 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12027 if (!have_row || svn_sqlite__column_revnum(stmt, 0))
12028 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
12029 svn_sqlite__reset(stmt),
12030 _("The base node '%s' was not found."),
12031 path_for_error_message(wcroot,
12034 repos_path = svn_sqlite__column_text(stmt, 2, scratch_pool);
12035 revision = svn_sqlite__column_revnum(stmt, 3);
12038 SVN_ERR(svn_sqlite__reset(stmt));
12042 svn_boolean_t have_row2;
12044 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12045 STMT_SELECT_MOVED_HERE));
12046 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, node_moved_to,
12047 relpath_depth(node_moved_to)));
12048 SVN_ERR(svn_sqlite__step(&have_row2, stmt));
12049 if (!have_row2 || !svn_sqlite__column_int(stmt, 0)
12050 || revision != svn_sqlite__column_revnum(stmt, 3)
12051 || strcmp(repos_path, svn_sqlite__column_text(stmt, 2, NULL)))
12052 node_moved_to = NULL;
12053 SVN_ERR(svn_sqlite__reset(stmt));
12058 struct svn_wc__db_moved_to_t *moved_to;
12060 moved_to = apr_palloc(result_pool, sizeof(*moved_to));
12061 moved_to->op_depth = working_op_depth;
12062 moved_to->local_relpath = node_moved_to;
12063 APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
12066 /* A working row with moved_to, or no working row, and we are done. */
12067 if (node_moved_to || !have_row)
12068 return SVN_NO_ERROR;
12070 /* Need to handle being moved via an ancestor. */
12071 ancestor_relpath = local_relpath;
12072 for (i = relpath_depth(local_relpath); i > working_op_depth; --i)
12074 const char *ancestor_moved_to;
12076 ancestor_relpath = svn_relpath_dirname(ancestor_relpath, scratch_pool);
12078 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12079 STMT_SELECT_MOVED_TO));
12080 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, ancestor_relpath,
12081 working_op_depth));
12082 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12083 SVN_ERR_ASSERT(have_row);
12084 ancestor_moved_to = svn_sqlite__column_text(stmt, 0, scratch_pool);
12085 SVN_ERR(svn_sqlite__reset(stmt));
12086 if (ancestor_moved_to)
12089 = svn_relpath_join(ancestor_moved_to,
12090 svn_relpath_skip_ancestor(ancestor_relpath,
12094 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12095 STMT_SELECT_MOVED_HERE));
12096 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, node_moved_to,
12097 relpath_depth(ancestor_moved_to)));
12098 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12100 ancestor_moved_to = NULL;
12101 else if (!svn_sqlite__column_int(stmt, 0))
12103 svn_wc__db_status_t presence
12104 = svn_sqlite__column_token(stmt, 1, presence_map);
12105 if (presence != svn_wc__db_status_not_present)
12106 ancestor_moved_to = NULL;
12109 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12110 if (!have_row && !svn_sqlite__column_int(stmt, 0))
12111 ancestor_moved_to = NULL;
12114 SVN_ERR(svn_sqlite__reset(stmt));
12115 if (!ancestor_moved_to)
12117 /* verify repos_path points back? */
12119 if (ancestor_moved_to)
12121 struct svn_wc__db_moved_to_t *moved_to;
12123 moved_to = apr_palloc(result_pool, sizeof(*moved_to));
12124 moved_to->op_depth = working_op_depth;
12125 moved_to->local_relpath = node_moved_to;
12126 APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
12128 SVN_ERR(follow_moved_to(moved_tos, relpath_depth(ancestor_moved_to),
12129 repos_path, revision, wcroot, node_moved_to,
12130 result_pool, scratch_pool));
12135 return SVN_NO_ERROR;
12139 svn_wc__db_follow_moved_to(apr_array_header_t **moved_tos,
12141 const char *local_abspath,
12142 apr_pool_t *result_pool,
12143 apr_pool_t *scratch_pool)
12145 svn_wc__db_wcroot_t *wcroot;
12146 const char *local_relpath;
12148 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12150 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12151 local_abspath, scratch_pool, scratch_pool));
12152 VERIFY_USABLE_WCROOT(wcroot);
12154 *moved_tos = apr_array_make(result_pool, 0,
12155 sizeof(struct svn_wc__db_moved_to_t *));
12157 /* ### Wrap in a transaction */
12158 SVN_ERR(follow_moved_to(moved_tos, 0, NULL, SVN_INVALID_REVNUM,
12159 wcroot, local_relpath,
12160 result_pool, scratch_pool));
12162 /* ### Convert moved_to to abspath */
12164 return SVN_NO_ERROR;
12167 /* Extract the moved-to information for LOCAL_RELPATH at OP-DEPTH by
12168 examining the lowest working node above OP_DEPTH. The output paths
12169 are NULL if there is no move, otherwise:
12171 *MOVE_DST_RELPATH: the moved-to destination of LOCAL_RELPATH.
12173 *MOVE_DST_OP_ROOT_RELPATH: the moved-to destination of the root of
12174 the move of LOCAL_RELPATH. This may be equal to *MOVE_DST_RELPATH
12175 if LOCAL_RELPATH is the root of the move.
12177 *MOVE_SRC_ROOT_RELPATH: the root of the move source. For moves
12178 inside a delete this will be different from *MOVE_SRC_OP_ROOT_RELPATH.
12180 *MOVE_SRC_OP_ROOT_RELPATH: the root of the source layer that
12181 contains the move. For moves inside deletes this is the root of
12182 the delete, for other moves this is the root of the move.
12184 Given a path A/B/C with A/B moved to X then for A/B/C
12186 MOVE_DST_RELPATH is X/C
12187 MOVE_DST_OP_ROOT_RELPATH is X
12188 MOVE_SRC_ROOT_RELPATH is A/B
12189 MOVE_SRC_OP_ROOT_RELPATH is A/B
12191 If A is then deleted the MOVE_DST_RELPATH, MOVE_DST_OP_ROOT_RELPATH
12192 and MOVE_SRC_ROOT_RELPATH remain the same but MOVE_SRC_OP_ROOT_RELPATH
12195 ### Think about combining with scan_deletion? Also with
12196 ### scan_addition to get moved-to for replaces? Do we need to
12197 ### return the op-root of the move source, i.e. A/B in the example
12200 svn_wc__db_op_depth_moved_to(const char **move_dst_relpath,
12201 const char **move_dst_op_root_relpath,
12202 const char **move_src_root_relpath,
12203 const char **move_src_op_root_relpath,
12205 svn_wc__db_wcroot_t *wcroot,
12206 const char *local_relpath,
12207 apr_pool_t *result_pool,
12208 apr_pool_t *scratch_pool)
12210 svn_sqlite__stmt_t *stmt;
12211 svn_boolean_t have_row;
12212 int delete_op_depth;
12213 const char *relpath = local_relpath;
12215 *move_dst_relpath = *move_dst_op_root_relpath = NULL;
12216 *move_src_root_relpath = *move_src_op_root_relpath = NULL;
12220 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12221 STMT_SELECT_LOWEST_WORKING_NODE));
12222 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath, op_depth));
12223 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12226 delete_op_depth = svn_sqlite__column_int(stmt, 0);
12227 *move_dst_op_root_relpath = svn_sqlite__column_text(stmt, 3,
12229 if (*move_dst_op_root_relpath)
12230 *move_src_root_relpath = apr_pstrdup(result_pool, relpath);
12232 SVN_ERR(svn_sqlite__reset(stmt));
12233 if (!*move_dst_op_root_relpath)
12234 relpath = svn_relpath_dirname(relpath, scratch_pool);
12236 while (!*move_dst_op_root_relpath
12237 && have_row && delete_op_depth <= relpath_depth(relpath));
12239 if (*move_dst_op_root_relpath)
12242 = svn_relpath_join(*move_dst_op_root_relpath,
12243 svn_relpath_skip_ancestor(relpath, local_relpath),
12245 while (delete_op_depth < relpath_depth(relpath))
12246 relpath = svn_relpath_dirname(relpath, scratch_pool);
12247 *move_src_op_root_relpath = apr_pstrdup(result_pool, relpath);
12250 return SVN_NO_ERROR;
12253 /* Public (within libsvn_wc) absolute path version of
12254 svn_wc__db_op_depth_moved_to with the op-depth hard-coded to
12257 svn_wc__db_base_moved_to(const char **move_dst_abspath,
12258 const char **move_dst_op_root_abspath,
12259 const char **move_src_root_abspath,
12260 const char **move_src_op_root_abspath,
12262 const char *local_abspath,
12263 apr_pool_t *result_pool,
12264 apr_pool_t *scratch_pool)
12266 svn_wc__db_wcroot_t *wcroot;
12267 const char *local_relpath;
12268 const char *move_dst_relpath, *move_dst_op_root_relpath;
12269 const char *move_src_root_relpath, *move_src_op_root_relpath;
12271 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12273 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12274 local_abspath, scratch_pool, scratch_pool));
12275 VERIFY_USABLE_WCROOT(wcroot);
12277 SVN_WC__DB_WITH_TXN(svn_wc__db_op_depth_moved_to(&move_dst_relpath,
12278 &move_dst_op_root_relpath,
12279 &move_src_root_relpath,
12280 &move_src_op_root_relpath,
12281 0 /* BASE op-depth */,
12282 wcroot, local_relpath,
12283 scratch_pool, scratch_pool),
12286 if (move_dst_abspath)
12289 ? svn_dirent_join(wcroot->abspath, move_dst_relpath, result_pool)
12292 if (move_dst_op_root_abspath)
12293 *move_dst_op_root_abspath
12294 = move_dst_op_root_relpath
12295 ? svn_dirent_join(wcroot->abspath, move_dst_op_root_relpath, result_pool)
12298 if (move_src_root_abspath)
12299 *move_src_root_abspath
12300 = move_src_root_relpath
12301 ? svn_dirent_join(wcroot->abspath, move_src_root_relpath, result_pool)
12304 if (move_src_op_root_abspath)
12305 *move_src_op_root_abspath
12306 = move_src_op_root_relpath
12307 ? svn_dirent_join(wcroot->abspath, move_src_op_root_relpath, result_pool)
12310 return SVN_NO_ERROR;
12314 svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb,
12315 apr_int64_t *repos_id,
12316 apr_int64_t *wc_id,
12317 svn_wc__db_t *wc_db,
12318 const char *dir_abspath,
12319 const char *repos_root_url,
12320 const char *repos_uuid,
12321 apr_pool_t *scratch_pool)
12323 svn_wc__db_wcroot_t *wcroot;
12325 /* Upgrade is inherently exclusive so specify exclusive locking. */
12326 SVN_ERR(create_db(sdb, repos_id, wc_id, dir_abspath,
12327 repos_root_url, repos_uuid,
12329 NULL, SVN_INVALID_REVNUM, svn_depth_unknown,
12330 TRUE /* exclusive */,
12331 wc_db->state_pool, scratch_pool));
12333 SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
12334 apr_pstrdup(wc_db->state_pool,
12336 *sdb, *wc_id, FORMAT_FROM_SDB,
12337 FALSE /* auto-upgrade */,
12338 FALSE /* enforce_empty_wq */,
12339 wc_db->state_pool, scratch_pool));
12341 /* The WCROOT is complete. Stash it into DB. */
12342 svn_hash_sets(wc_db->dir_data, wcroot->abspath, wcroot);
12344 return SVN_NO_ERROR;
12349 svn_wc__db_upgrade_apply_dav_cache(svn_sqlite__db_t *sdb,
12350 const char *dir_relpath,
12351 apr_hash_t *cache_values,
12352 apr_pool_t *scratch_pool)
12354 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
12356 apr_hash_index_t *hi;
12357 svn_sqlite__stmt_t *stmt;
12359 SVN_ERR(svn_wc__db_util_fetch_wc_id(&wc_id, sdb, iterpool));
12361 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12362 STMT_UPDATE_BASE_NODE_DAV_CACHE));
12364 /* Iterate over all the wcprops, writing each one to the wc_db. */
12365 for (hi = apr_hash_first(scratch_pool, cache_values);
12367 hi = apr_hash_next(hi))
12369 const char *name = svn__apr_hash_index_key(hi);
12370 apr_hash_t *props = svn__apr_hash_index_val(hi);
12371 const char *local_relpath;
12373 svn_pool_clear(iterpool);
12375 local_relpath = svn_relpath_join(dir_relpath, name, iterpool);
12377 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
12378 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, iterpool));
12379 SVN_ERR(svn_sqlite__step_done(stmt));
12382 svn_pool_destroy(iterpool);
12384 return SVN_NO_ERROR;
12389 svn_wc__db_upgrade_apply_props(svn_sqlite__db_t *sdb,
12390 const char *dir_abspath,
12391 const char *local_relpath,
12392 apr_hash_t *base_props,
12393 apr_hash_t *revert_props,
12394 apr_hash_t *working_props,
12395 int original_format,
12397 apr_pool_t *scratch_pool)
12399 svn_sqlite__stmt_t *stmt;
12400 svn_boolean_t have_row;
12401 int top_op_depth = -1;
12402 int below_op_depth = -1;
12403 svn_wc__db_status_t top_presence;
12404 svn_wc__db_status_t below_presence;
12407 /* ### working_props: use set_props_txn.
12408 ### if working_props == NULL, then skip. what if they equal the
12409 ### pristine props? we should probably do the compare here.
12411 ### base props go into WORKING_NODE if avail, otherwise BASE.
12413 ### revert only goes into BASE. (and WORKING better be there!)
12415 Prior to 1.4.0 (ORIGINAL_FORMAT < 8), REVERT_PROPS did not exist. If a
12416 file was deleted, then a copy (potentially with props) was disallowed
12417 and could not replace the deletion. An addition *could* be performed,
12418 but that would never bring its own props.
12420 1.4.0 through 1.4.5 created the concept of REVERT_PROPS, but had a
12421 bug in svn_wc_add_repos_file2() whereby a copy-with-props did NOT
12422 construct a REVERT_PROPS if the target had no props. Thus, reverting
12423 the delete/copy would see no REVERT_PROPS to restore, leaving the
12424 props from the copy source intact, and appearing as if they are (now)
12425 the base props for the previously-deleted file. (wc corruption)
12427 1.4.6 ensured that an empty REVERT_PROPS would be established at all
12428 times. See issue 2530, and r861670 as starting points.
12430 We will use ORIGINAL_FORMAT and SVN_WC__NO_REVERT_FILES to determine
12431 the handling of our inputs, relative to the state of this node.
12434 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_NODE_INFO));
12435 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
12436 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12439 top_op_depth = svn_sqlite__column_int(stmt, 0);
12440 top_presence = svn_sqlite__column_token(stmt, 3, presence_map);
12441 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12444 below_op_depth = svn_sqlite__column_int(stmt, 0);
12445 below_presence = svn_sqlite__column_token(stmt, 3, presence_map);
12448 SVN_ERR(svn_sqlite__reset(stmt));
12450 /* Detect the buggy scenario described above. We cannot upgrade this
12451 working copy if we have no idea where BASE_PROPS should go. */
12452 if (original_format > SVN_WC__NO_REVERT_FILES
12453 && revert_props == NULL
12454 && top_op_depth != -1
12455 && top_presence == svn_wc__db_status_normal
12456 && below_op_depth != -1
12457 && below_presence != svn_wc__db_status_not_present)
12459 /* There should be REVERT_PROPS, so it appears that we just ran into
12460 the described bug. Sigh. */
12461 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
12462 _("The properties of '%s' are in an "
12463 "indeterminate state and cannot be "
12464 "upgraded. See issue #2530."),
12465 svn_dirent_local_style(
12466 svn_dirent_join(dir_abspath, local_relpath,
12467 scratch_pool), scratch_pool));
12470 /* Need at least one row, or two rows if there are revert props */
12471 if (top_op_depth == -1
12472 || (below_op_depth == -1 && revert_props))
12473 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
12474 _("Insufficient NODES rows for '%s'"),
12475 svn_dirent_local_style(
12476 svn_dirent_join(dir_abspath, local_relpath,
12477 scratch_pool), scratch_pool));
12479 /* one row, base props only: upper row gets base props
12480 two rows, base props only: lower row gets base props
12481 two rows, revert props only: lower row gets revert props
12482 two rows, base and revert props: upper row gets base, lower gets revert */
12485 if (revert_props || below_op_depth == -1)
12487 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12488 STMT_UPDATE_NODE_PROPS));
12489 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
12490 wc_id, local_relpath, top_op_depth));
12491 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, base_props, scratch_pool));
12492 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
12494 SVN_ERR_ASSERT(affected_rows == 1);
12497 if (below_op_depth != -1)
12499 apr_hash_t *props = revert_props ? revert_props : base_props;
12501 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12502 STMT_UPDATE_NODE_PROPS));
12503 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
12504 wc_id, local_relpath, below_op_depth));
12505 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
12506 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
12508 SVN_ERR_ASSERT(affected_rows == 1);
12511 /* If there are WORKING_PROPS, then they always go into ACTUAL_NODE. */
12512 if (working_props != NULL
12513 && base_props != NULL)
12515 apr_array_header_t *diffs;
12517 SVN_ERR(svn_prop_diffs(&diffs, working_props, base_props, scratch_pool));
12519 if (diffs->nelts == 0)
12520 working_props = NULL; /* No differences */
12523 if (working_props != NULL)
12525 SVN_ERR(set_actual_props(wc_id, local_relpath, working_props,
12526 sdb, scratch_pool));
12529 return SVN_NO_ERROR;
12533 svn_wc__db_upgrade_insert_external(svn_wc__db_t *db,
12534 const char *local_abspath,
12535 svn_node_kind_t kind,
12536 const char *parent_abspath,
12537 const char *def_local_abspath,
12538 const char *repos_relpath,
12539 const char *repos_root_url,
12540 const char *repos_uuid,
12541 svn_revnum_t def_peg_revision,
12542 svn_revnum_t def_revision,
12543 apr_pool_t *scratch_pool)
12545 svn_wc__db_wcroot_t *wcroot;
12546 const char *def_local_relpath;
12547 svn_sqlite__stmt_t *stmt;
12548 svn_boolean_t have_row;
12549 apr_int64_t repos_id;
12551 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12553 /* We know only of DEF_LOCAL_ABSPATH that it definitely belongs to "this"
12554 * WC, i.e. where the svn:externals prop is set. The external target path
12555 * itself may be "hidden behind" other working copies. */
12556 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &def_local_relpath,
12557 db, def_local_abspath,
12558 scratch_pool, scratch_pool));
12559 VERIFY_USABLE_WCROOT(wcroot);
12562 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12563 STMT_SELECT_REPOSITORY));
12564 SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
12565 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12568 repos_id = svn_sqlite__column_int64(stmt, 0);
12569 SVN_ERR(svn_sqlite__reset(stmt));
12573 /* Need to set up a new repository row. */
12574 SVN_ERR(create_repos_id(&repos_id, repos_root_url, repos_uuid,
12575 wcroot->sdb, scratch_pool));
12578 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12579 STMT_INSERT_EXTERNAL));
12581 /* wc_id, local_relpath, parent_relpath, presence, kind, def_local_relpath,
12582 * repos_id, def_repos_relpath, def_operational_revision, def_revision */
12583 SVN_ERR(svn_sqlite__bindf(stmt, "issstsis",
12585 svn_dirent_skip_ancestor(wcroot->abspath,
12587 svn_dirent_skip_ancestor(wcroot->abspath,
12595 if (SVN_IS_VALID_REVNUM(def_peg_revision))
12596 SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, def_peg_revision));
12598 if (SVN_IS_VALID_REVNUM(def_revision))
12599 SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, def_revision));
12601 SVN_ERR(svn_sqlite__insert(NULL, stmt));
12603 return SVN_NO_ERROR;
12607 svn_wc__db_upgrade_get_repos_id(apr_int64_t *repos_id,
12608 svn_sqlite__db_t *sdb,
12609 const char *repos_root_url,
12610 apr_pool_t *scratch_pool)
12612 svn_sqlite__stmt_t *stmt;
12613 svn_boolean_t have_row;
12615 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_REPOSITORY));
12616 SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
12617 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12620 return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt),
12621 _("Repository '%s' not found in the database"),
12624 *repos_id = svn_sqlite__column_int64(stmt, 0);
12625 return svn_error_trace(svn_sqlite__reset(stmt));
12630 svn_wc__db_wq_add(svn_wc__db_t *db,
12631 const char *wri_abspath,
12632 const svn_skel_t *work_item,
12633 apr_pool_t *scratch_pool)
12635 svn_wc__db_wcroot_t *wcroot;
12636 const char *local_relpath;
12638 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
12640 /* Quick exit, if there are no work items to queue up. */
12641 if (work_item == NULL)
12642 return SVN_NO_ERROR;
12644 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12645 wri_abspath, scratch_pool, scratch_pool));
12646 VERIFY_USABLE_WCROOT(wcroot);
12648 /* Add the work item(s) to the WORK_QUEUE. */
12649 return svn_error_trace(add_work_items(wcroot->sdb, work_item,
12653 /* The body of svn_wc__db_wq_fetch_next().
12655 static svn_error_t *
12656 wq_fetch_next(apr_uint64_t *id,
12657 svn_skel_t **work_item,
12658 svn_wc__db_wcroot_t *wcroot,
12659 const char *local_relpath,
12660 apr_uint64_t completed_id,
12661 apr_pool_t *result_pool,
12662 apr_pool_t *scratch_pool)
12664 svn_sqlite__stmt_t *stmt;
12665 svn_boolean_t have_row;
12667 if (completed_id != 0)
12669 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12670 STMT_DELETE_WORK_ITEM));
12671 SVN_ERR(svn_sqlite__bind_int64(stmt, 1, completed_id));
12673 SVN_ERR(svn_sqlite__step_done(stmt));
12676 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12677 STMT_SELECT_WORK_ITEM));
12678 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12690 *id = svn_sqlite__column_int64(stmt, 0);
12692 val = svn_sqlite__column_blob(stmt, 1, &len, result_pool);
12694 *work_item = svn_skel__parse(val, len, result_pool);
12697 return svn_error_trace(svn_sqlite__reset(stmt));
12701 svn_wc__db_wq_fetch_next(apr_uint64_t *id,
12702 svn_skel_t **work_item,
12704 const char *wri_abspath,
12705 apr_uint64_t completed_id,
12706 apr_pool_t *result_pool,
12707 apr_pool_t *scratch_pool)
12709 svn_wc__db_wcroot_t *wcroot;
12710 const char *local_relpath;
12712 SVN_ERR_ASSERT(id != NULL);
12713 SVN_ERR_ASSERT(work_item != NULL);
12714 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
12716 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12717 wri_abspath, scratch_pool, scratch_pool));
12718 VERIFY_USABLE_WCROOT(wcroot);
12720 SVN_WC__DB_WITH_TXN(
12721 wq_fetch_next(id, work_item,
12722 wcroot, local_relpath, completed_id,
12723 result_pool, scratch_pool),
12726 return SVN_NO_ERROR;
12729 /* Records timestamp and date for one or more files in wcroot */
12730 static svn_error_t *
12731 wq_record(svn_wc__db_wcroot_t *wcroot,
12732 apr_hash_t *record_map,
12733 apr_pool_t *scratch_pool)
12735 apr_hash_index_t *hi;
12736 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
12738 for (hi = apr_hash_first(scratch_pool, record_map); hi;
12739 hi = apr_hash_next(hi))
12741 const char *local_abspath = svn__apr_hash_index_key(hi);
12742 const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
12743 const char *local_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
12746 svn_pool_clear(iterpool);
12748 if (! local_relpath)
12751 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
12752 dirent->filesize, dirent->mtime,
12756 svn_pool_destroy(iterpool);
12757 return SVN_NO_ERROR;
12761 svn_wc__db_wq_record_and_fetch_next(apr_uint64_t *id,
12762 svn_skel_t **work_item,
12764 const char *wri_abspath,
12765 apr_uint64_t completed_id,
12766 apr_hash_t *record_map,
12767 apr_pool_t *result_pool,
12768 apr_pool_t *scratch_pool)
12770 svn_wc__db_wcroot_t *wcroot;
12771 const char *local_relpath;
12773 SVN_ERR_ASSERT(id != NULL);
12774 SVN_ERR_ASSERT(work_item != NULL);
12775 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
12777 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12778 wri_abspath, scratch_pool, scratch_pool));
12779 VERIFY_USABLE_WCROOT(wcroot);
12781 SVN_WC__DB_WITH_TXN(
12782 svn_error_compose_create(
12783 wq_fetch_next(id, work_item,
12784 wcroot, local_relpath, completed_id,
12785 result_pool, scratch_pool),
12786 wq_record(wcroot, record_map, scratch_pool)),
12789 return SVN_NO_ERROR;
12794 /* ### temporary API. remove before release. */
12796 svn_wc__db_temp_get_format(int *format,
12798 const char *local_dir_abspath,
12799 apr_pool_t *scratch_pool)
12801 svn_wc__db_wcroot_t *wcroot;
12802 const char *local_relpath;
12805 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
12806 /* ### assert that we were passed a directory? */
12808 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12809 local_dir_abspath, scratch_pool, scratch_pool);
12811 /* If we hit an error examining this directory, then declare this
12812 directory to not be a working copy. */
12815 if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
12816 return svn_error_trace(err);
12817 svn_error_clear(err);
12819 /* Remap the returned error. */
12821 return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
12822 _("'%s' is not a working copy"),
12823 svn_dirent_local_style(local_dir_abspath,
12827 SVN_ERR_ASSERT(wcroot != NULL);
12828 SVN_ERR_ASSERT(wcroot->format >= 1);
12830 *format = wcroot->format;
12832 return SVN_NO_ERROR;
12835 /* ### temporary API. remove before release. */
12836 svn_wc_adm_access_t *
12837 svn_wc__db_temp_get_access(svn_wc__db_t *db,
12838 const char *local_dir_abspath,
12839 apr_pool_t *scratch_pool)
12841 const char *local_relpath;
12842 svn_wc__db_wcroot_t *wcroot;
12845 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
12847 /* ### we really need to assert that we were passed a directory. sometimes
12848 ### adm_retrieve_internal is asked about a file, and then it asks us
12849 ### for an access baton for it. we should definitely return NULL, but
12850 ### ideally: the caller would never ask us about a non-directory. */
12852 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
12853 db, local_dir_abspath, scratch_pool, scratch_pool);
12856 svn_error_clear(err);
12863 return svn_hash_gets(wcroot->access_cache, local_dir_abspath);
12867 /* ### temporary API. remove before release. */
12869 svn_wc__db_temp_set_access(svn_wc__db_t *db,
12870 const char *local_dir_abspath,
12871 svn_wc_adm_access_t *adm_access,
12872 apr_pool_t *scratch_pool)
12874 const char *local_relpath;
12875 svn_wc__db_wcroot_t *wcroot;
12878 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
12879 /* ### assert that we were passed a directory? */
12881 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
12882 db, local_dir_abspath, scratch_pool, scratch_pool);
12885 /* We don't even have a wcroot, so just bail. */
12886 svn_error_clear(err);
12890 /* Better not override something already there. */
12891 SVN_ERR_ASSERT_NO_RETURN(
12892 svn_hash_gets(wcroot->access_cache, local_dir_abspath) == NULL
12894 svn_hash_sets(wcroot->access_cache, local_dir_abspath, adm_access);
12898 /* ### temporary API. remove before release. */
12900 svn_wc__db_temp_close_access(svn_wc__db_t *db,
12901 const char *local_dir_abspath,
12902 svn_wc_adm_access_t *adm_access,
12903 apr_pool_t *scratch_pool)
12905 const char *local_relpath;
12906 svn_wc__db_wcroot_t *wcroot;
12908 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
12909 /* ### assert that we were passed a directory? */
12911 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12912 local_dir_abspath, scratch_pool, scratch_pool));
12913 svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
12915 return SVN_NO_ERROR;
12919 /* ### temporary API. remove before release. */
12921 svn_wc__db_temp_clear_access(svn_wc__db_t *db,
12922 const char *local_dir_abspath,
12923 apr_pool_t *scratch_pool)
12925 const char *local_relpath;
12926 svn_wc__db_wcroot_t *wcroot;
12929 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
12930 /* ### assert that we were passed a directory? */
12932 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
12933 db, local_dir_abspath, scratch_pool, scratch_pool);
12936 svn_error_clear(err);
12940 svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
12945 svn_wc__db_temp_get_all_access(svn_wc__db_t *db,
12946 apr_pool_t *result_pool)
12948 apr_hash_t *result = apr_hash_make(result_pool);
12949 apr_hash_index_t *hi;
12951 for (hi = apr_hash_first(result_pool, db->dir_data);
12953 hi = apr_hash_next(hi))
12955 const svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi);
12957 /* This is highly redundant, 'cause the same WCROOT will appear many
12958 times in dir_data. */
12959 result = apr_hash_overlay(result_pool, result, wcroot->access_cache);
12967 svn_wc__db_temp_borrow_sdb(svn_sqlite__db_t **sdb,
12969 const char *local_dir_abspath,
12970 apr_pool_t *scratch_pool)
12972 svn_wc__db_wcroot_t *wcroot;
12973 const char *local_relpath;
12975 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
12977 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12978 local_dir_abspath, scratch_pool, scratch_pool));
12979 VERIFY_USABLE_WCROOT(wcroot);
12981 *sdb = wcroot->sdb;
12983 return SVN_NO_ERROR;
12988 svn_wc__db_read_conflict_victims(const apr_array_header_t **victims,
12990 const char *local_abspath,
12991 apr_pool_t *result_pool,
12992 apr_pool_t *scratch_pool)
12994 svn_wc__db_wcroot_t *wcroot;
12995 const char *local_relpath;
12996 svn_sqlite__stmt_t *stmt;
12997 svn_boolean_t have_row;
12998 apr_array_header_t *new_victims;
13000 /* The parent should be a working copy directory. */
13001 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13002 local_abspath, scratch_pool, scratch_pool));
13003 VERIFY_USABLE_WCROOT(wcroot);
13005 /* ### This will be much easier once we have all conflicts in one
13008 /* Look for text, tree and property conflicts in ACTUAL */
13009 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13010 STMT_SELECT_CONFLICT_VICTIMS));
13011 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13013 new_victims = apr_array_make(result_pool, 0, sizeof(const char *));
13015 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13018 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
13020 APR_ARRAY_PUSH(new_victims, const char *) =
13021 svn_relpath_basename(child_relpath, result_pool);
13023 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13026 SVN_ERR(svn_sqlite__reset(stmt));
13028 *victims = new_victims;
13029 return SVN_NO_ERROR;
13032 /* The body of svn_wc__db_get_conflict_marker_files().
13034 static svn_error_t *
13035 get_conflict_marker_files(apr_hash_t **marker_files_p,
13036 svn_wc__db_wcroot_t *wcroot,
13037 const char *local_relpath,
13039 apr_pool_t *result_pool,
13040 apr_pool_t *scratch_pool)
13042 svn_sqlite__stmt_t *stmt;
13043 svn_boolean_t have_row;
13044 apr_hash_t *marker_files = apr_hash_make(result_pool);
13046 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13047 STMT_SELECT_ACTUAL_NODE));
13048 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13049 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13051 if (have_row && !svn_sqlite__column_is_null(stmt, 2))
13054 const void *data = svn_sqlite__column_blob(stmt, 2, &len, NULL);
13055 svn_skel_t *conflicts;
13056 const apr_array_header_t *markers;
13059 conflicts = svn_skel__parse(data, len, scratch_pool);
13061 /* ### ADD markers to *marker_files */
13062 SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13064 result_pool, scratch_pool));
13066 for (i = 0; markers && (i < markers->nelts); i++)
13068 const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13070 svn_hash_sets(marker_files, marker_abspath, "");
13073 SVN_ERR(svn_sqlite__reset(stmt));
13075 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13076 STMT_SELECT_CONFLICT_VICTIMS));
13077 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13078 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13083 const void *data = svn_sqlite__column_blob(stmt, 1, &len, NULL);
13085 const apr_array_header_t *markers;
13090 svn_skel_t *conflicts;
13091 conflicts = svn_skel__parse(data, len, scratch_pool);
13093 SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13095 result_pool, scratch_pool));
13097 for (i = 0; markers && (i < markers->nelts); i++)
13099 const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13101 svn_hash_sets(marker_files, marker_abspath, "");
13105 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13108 if (apr_hash_count(marker_files))
13109 *marker_files_p = marker_files;
13111 *marker_files_p = NULL;
13113 return svn_error_trace(svn_sqlite__reset(stmt));
13117 svn_wc__db_get_conflict_marker_files(apr_hash_t **marker_files,
13119 const char *local_abspath,
13120 apr_pool_t *result_pool,
13121 apr_pool_t *scratch_pool)
13123 svn_wc__db_wcroot_t *wcroot;
13124 const char *local_relpath;
13126 /* The parent should be a working copy directory. */
13127 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13128 local_abspath, scratch_pool, scratch_pool));
13129 VERIFY_USABLE_WCROOT(wcroot);
13131 SVN_WC__DB_WITH_TXN(
13132 get_conflict_marker_files(marker_files, wcroot, local_relpath, db,
13133 result_pool, scratch_pool),
13136 return SVN_NO_ERROR;
13141 svn_wc__db_read_conflict(svn_skel_t **conflict,
13143 const char *local_abspath,
13144 apr_pool_t *result_pool,
13145 apr_pool_t *scratch_pool)
13147 svn_wc__db_wcroot_t *wcroot;
13148 const char *local_relpath;
13150 /* The parent should be a working copy directory. */
13151 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13152 local_abspath, scratch_pool, scratch_pool));
13153 VERIFY_USABLE_WCROOT(wcroot);
13155 return svn_error_trace(svn_wc__db_read_conflict_internal(conflict, wcroot,
13162 svn_wc__db_read_conflict_internal(svn_skel_t **conflict,
13163 svn_wc__db_wcroot_t *wcroot,
13164 const char *local_relpath,
13165 apr_pool_t *result_pool,
13166 apr_pool_t *scratch_pool)
13168 svn_sqlite__stmt_t *stmt;
13169 svn_boolean_t have_row;
13171 /* Check if we have a conflict in ACTUAL */
13172 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13173 STMT_SELECT_ACTUAL_NODE));
13174 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13176 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13180 /* Do this while stmt is still open to avoid closing the sqlite
13181 transaction and then reopening. */
13182 svn_sqlite__stmt_t *stmt_node;
13185 err = svn_sqlite__get_statement(&stmt_node, wcroot->sdb,
13186 STMT_SELECT_NODE_INFO);
13191 err = svn_sqlite__bindf(stmt_node, "is", wcroot->wc_id,
13195 err = svn_sqlite__step(&have_row, stmt_node);
13198 err = svn_error_compose_create(err,
13199 svn_sqlite__reset(stmt_node));
13201 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
13206 return SVN_NO_ERROR;
13209 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13210 _("The node '%s' was not found."),
13211 path_for_error_message(wcroot,
13217 apr_size_t cfl_len;
13218 const void *cfl_data;
13220 /* svn_skel__parse doesn't copy data, so store in result_pool */
13221 cfl_data = svn_sqlite__column_blob(stmt, 2, &cfl_len, result_pool);
13224 *conflict = svn_skel__parse(cfl_data, cfl_len, result_pool);
13228 return svn_error_trace(svn_sqlite__reset(stmt));
13234 svn_wc__db_read_kind(svn_node_kind_t *kind,
13236 const char *local_abspath,
13237 svn_boolean_t allow_missing,
13238 svn_boolean_t show_deleted,
13239 svn_boolean_t show_hidden,
13240 apr_pool_t *scratch_pool)
13242 svn_wc__db_wcroot_t *wcroot;
13243 const char *local_relpath;
13244 svn_sqlite__stmt_t *stmt_info;
13245 svn_boolean_t have_info;
13247 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13249 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13250 local_abspath, scratch_pool, scratch_pool));
13251 VERIFY_USABLE_WCROOT(wcroot);
13253 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
13254 STMT_SELECT_NODE_INFO));
13255 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
13256 SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
13262 *kind = svn_node_unknown;
13263 SVN_ERR(svn_sqlite__reset(stmt_info));
13264 return SVN_NO_ERROR;
13268 SVN_ERR(svn_sqlite__reset(stmt_info));
13269 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13270 _("The node '%s' was not found."),
13271 path_for_error_message(wcroot,
13277 if (!(show_deleted && show_hidden))
13279 int op_depth = svn_sqlite__column_int(stmt_info, 0);
13280 svn_boolean_t report_none = FALSE;
13281 svn_wc__db_status_t status = svn_sqlite__column_token(stmt_info, 3,
13285 SVN_ERR(convert_to_working_status(&status, status));
13289 case svn_wc__db_status_not_present:
13290 if (! (show_hidden && show_deleted))
13291 report_none = TRUE;
13293 case svn_wc__db_status_excluded:
13294 case svn_wc__db_status_server_excluded:
13296 report_none = TRUE;
13298 case svn_wc__db_status_deleted:
13299 if (! show_deleted)
13300 report_none = TRUE;
13308 *kind = svn_node_none;
13309 return svn_error_trace(svn_sqlite__reset(stmt_info));
13313 *kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
13315 return svn_error_trace(svn_sqlite__reset(stmt_info));
13320 svn_wc__db_node_hidden(svn_boolean_t *hidden,
13322 const char *local_abspath,
13323 apr_pool_t *scratch_pool)
13325 svn_wc__db_wcroot_t *wcroot;
13326 const char *local_relpath;
13327 svn_wc__db_status_t status;
13329 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13331 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13332 local_abspath, scratch_pool, scratch_pool));
13333 VERIFY_USABLE_WCROOT(wcroot);
13335 SVN_ERR(read_info(&status, NULL, NULL, NULL, NULL, NULL,
13336 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13337 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13339 wcroot, local_relpath,
13340 scratch_pool, scratch_pool));
13342 *hidden = (status == svn_wc__db_status_server_excluded
13343 || status == svn_wc__db_status_not_present
13344 || status == svn_wc__db_status_excluded);
13346 return SVN_NO_ERROR;
13351 svn_wc__db_is_wcroot(svn_boolean_t *is_wcroot,
13353 const char *local_abspath,
13354 apr_pool_t *scratch_pool)
13356 svn_wc__db_wcroot_t *wcroot;
13357 const char *local_relpath;
13359 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13361 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13362 local_abspath, scratch_pool, scratch_pool));
13363 VERIFY_USABLE_WCROOT(wcroot);
13365 if (*local_relpath != '\0')
13367 *is_wcroot = FALSE; /* Node is a file, or has a parent directory within
13369 return SVN_NO_ERROR;
13374 return SVN_NO_ERROR;
13377 /* Find a node's kind and whether it is switched, putting the outputs in
13378 * *IS_SWITCHED and *KIND. Either of the outputs may be NULL if not wanted.
13380 static svn_error_t *
13381 db_is_switched(svn_boolean_t *is_switched,
13382 svn_node_kind_t *kind,
13383 svn_wc__db_wcroot_t *wcroot,
13384 const char *local_relpath,
13385 apr_pool_t *scratch_pool)
13387 svn_wc__db_status_t status;
13388 apr_int64_t repos_id;
13389 const char *repos_relpath;
13391 const char *parent_local_relpath;
13392 apr_int64_t parent_repos_id;
13393 const char *parent_repos_relpath;
13395 SVN_ERR_ASSERT(*local_relpath != '\0'); /* Handled in wrapper */
13397 SVN_ERR(read_info(&status, kind, NULL, &repos_relpath, &repos_id, NULL,
13398 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13399 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13400 wcroot, local_relpath, scratch_pool, scratch_pool));
13402 if (status == svn_wc__db_status_server_excluded
13403 || status == svn_wc__db_status_excluded
13404 || status == svn_wc__db_status_not_present)
13406 return svn_error_createf(
13407 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13408 _("The node '%s' was not found."),
13409 path_for_error_message(wcroot, local_relpath,
13412 else if (! repos_relpath)
13414 /* Node is shadowed; easy out */
13416 *is_switched = FALSE;
13418 return SVN_NO_ERROR;
13422 return SVN_NO_ERROR;
13424 svn_relpath_split(&parent_local_relpath, &name, local_relpath, scratch_pool);
13426 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
13427 &parent_repos_relpath,
13428 &parent_repos_id, NULL, NULL, NULL,
13429 NULL, NULL, NULL, NULL, NULL,
13431 wcroot, parent_local_relpath,
13432 scratch_pool, scratch_pool));
13434 if (repos_id != parent_repos_id)
13435 *is_switched = TRUE;
13438 const char *expected_relpath;
13440 expected_relpath = svn_relpath_join(parent_repos_relpath, name,
13443 *is_switched = (strcmp(expected_relpath, repos_relpath) != 0);
13446 return SVN_NO_ERROR;
13450 svn_wc__db_is_switched(svn_boolean_t *is_wcroot,
13451 svn_boolean_t *is_switched,
13452 svn_node_kind_t *kind,
13454 const char *local_abspath,
13455 apr_pool_t *scratch_pool)
13457 svn_wc__db_wcroot_t *wcroot;
13458 const char *local_relpath;
13460 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13462 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13463 local_abspath, scratch_pool, scratch_pool));
13464 VERIFY_USABLE_WCROOT(wcroot);
13467 *is_switched = FALSE;
13469 if (*local_relpath == '\0')
13476 *kind = svn_node_dir;
13477 return SVN_NO_ERROR;
13481 *is_wcroot = FALSE;
13483 if (! is_switched && ! kind)
13484 return SVN_NO_ERROR;
13486 SVN_WC__DB_WITH_TXN(
13487 db_is_switched(is_switched, kind, wcroot, local_relpath, scratch_pool),
13489 return SVN_NO_ERROR;
13494 svn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath,
13496 const char *wri_abspath,
13497 apr_pool_t *result_pool,
13498 apr_pool_t *scratch_pool)
13500 svn_wc__db_wcroot_t *wcroot;
13501 const char *local_relpath;
13503 SVN_ERR_ASSERT(temp_dir_abspath != NULL);
13504 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13506 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13507 wri_abspath, scratch_pool, scratch_pool));
13508 VERIFY_USABLE_WCROOT(wcroot);
13510 *temp_dir_abspath = svn_dirent_join_many(result_pool,
13512 svn_wc_get_adm_dir(scratch_pool),
13513 WCROOT_TEMPDIR_RELPATH,
13515 return SVN_NO_ERROR;
13519 /* Helper for wclock_obtain_cb() to steal an existing lock */
13520 static svn_error_t *
13521 wclock_steal(svn_wc__db_wcroot_t *wcroot,
13522 const char *local_relpath,
13523 apr_pool_t *scratch_pool)
13525 svn_sqlite__stmt_t *stmt;
13527 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_WC_LOCK));
13528 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13530 SVN_ERR(svn_sqlite__step_done(stmt));
13532 return SVN_NO_ERROR;
13536 /* The body of svn_wc__db_wclock_obtain().
13538 static svn_error_t *
13539 wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot,
13540 const char *local_relpath,
13541 int levels_to_lock,
13542 svn_boolean_t steal_lock,
13543 apr_pool_t *scratch_pool)
13545 svn_sqlite__stmt_t *stmt;
13547 const char *lock_relpath;
13550 svn_boolean_t got_row;
13552 svn_wc__db_wclock_t lock;
13554 /* Upgrade locks the root before the node exists. Apart from that
13555 the root node always exists so we will just skip the check.
13557 ### Perhaps the lock for upgrade should be created when the db is
13558 created? 1.6 used to lock .svn on creation. */
13559 if (local_relpath[0])
13561 svn_boolean_t exists;
13563 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
13565 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13566 _("The node '%s' was not found."),
13567 path_for_error_message(wcroot,
13572 /* Check if there are nodes locked below the new lock root */
13573 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_FIND_WC_LOCK));
13574 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13576 lock_depth = relpath_depth(local_relpath);
13577 max_depth = lock_depth + levels_to_lock;
13579 SVN_ERR(svn_sqlite__step(&got_row, stmt));
13583 svn_boolean_t own_lock;
13585 lock_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
13587 /* If we are not locking with depth infinity, check if this lock
13588 voids our lock request */
13589 if (levels_to_lock >= 0
13590 && relpath_depth(lock_relpath) > max_depth)
13592 SVN_ERR(svn_sqlite__step(&got_row, stmt));
13596 /* Check if we are the lock owner, because we should be able to
13597 extend our lock. */
13598 err = wclock_owns_lock(&own_lock, wcroot, lock_relpath,
13599 TRUE, scratch_pool);
13602 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
13604 if (!own_lock && !steal_lock)
13606 SVN_ERR(svn_sqlite__reset(stmt));
13607 err = svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
13608 _("'%s' is already locked."),
13609 path_for_error_message(wcroot,
13612 return svn_error_createf(SVN_ERR_WC_LOCKED, err,
13613 _("Working copy '%s' locked."),
13614 path_for_error_message(wcroot,
13618 else if (!own_lock)
13620 err = wclock_steal(wcroot, lock_relpath, scratch_pool);
13623 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
13626 SVN_ERR(svn_sqlite__step(&got_row, stmt));
13629 SVN_ERR(svn_sqlite__reset(stmt));
13632 SVN_ERR(wclock_steal(wcroot, local_relpath, scratch_pool));
13634 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_WC_LOCK));
13635 lock_relpath = local_relpath;
13639 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, lock_relpath));
13641 SVN_ERR(svn_sqlite__step(&got_row, stmt));
13645 int levels = svn_sqlite__column_int(stmt, 0);
13647 levels += relpath_depth(lock_relpath);
13649 SVN_ERR(svn_sqlite__reset(stmt));
13651 if (levels == -1 || levels >= lock_depth)
13654 err = svn_error_createf(
13655 SVN_ERR_WC_LOCKED, NULL,
13656 _("'%s' is already locked."),
13657 svn_dirent_local_style(
13658 svn_dirent_join(wcroot->abspath,
13662 return svn_error_createf(
13663 SVN_ERR_WC_LOCKED, err,
13664 _("Working copy '%s' locked."),
13665 path_for_error_message(wcroot,
13670 break; /* There can't be interesting locks on higher nodes */
13673 SVN_ERR(svn_sqlite__reset(stmt));
13675 if (!*lock_relpath)
13678 lock_relpath = svn_relpath_dirname(lock_relpath, scratch_pool);
13681 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_WC_LOCK));
13682 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
13684 err = svn_sqlite__insert(NULL, stmt);
13686 return svn_error_createf(SVN_ERR_WC_LOCKED, err,
13687 _("Working copy '%s' locked"),
13688 path_for_error_message(wcroot,
13692 /* And finally store that we obtained the lock */
13693 lock.local_relpath = apr_pstrdup(wcroot->owned_locks->pool, local_relpath);
13694 lock.levels = levels_to_lock;
13695 APR_ARRAY_PUSH(wcroot->owned_locks, svn_wc__db_wclock_t) = lock;
13697 return SVN_NO_ERROR;
13702 svn_wc__db_wclock_obtain(svn_wc__db_t *db,
13703 const char *local_abspath,
13704 int levels_to_lock,
13705 svn_boolean_t steal_lock,
13706 apr_pool_t *scratch_pool)
13708 svn_wc__db_wcroot_t *wcroot;
13709 const char *local_relpath;
13711 SVN_ERR_ASSERT(levels_to_lock >= -1);
13712 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13714 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13716 scratch_pool, scratch_pool));
13717 VERIFY_USABLE_WCROOT(wcroot);
13722 int depth = relpath_depth(local_relpath);
13724 for (i = 0; i < wcroot->owned_locks->nelts; i++)
13726 svn_wc__db_wclock_t* lock = &APR_ARRAY_IDX(wcroot->owned_locks,
13727 i, svn_wc__db_wclock_t);
13729 if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
13730 && (lock->levels == -1
13731 || (lock->levels + relpath_depth(lock->local_relpath))
13734 return svn_error_createf(
13735 SVN_ERR_WC_LOCKED, NULL,
13736 _("'%s' is already locked via '%s'."),
13737 svn_dirent_local_style(local_abspath, scratch_pool),
13738 path_for_error_message(wcroot, lock->local_relpath,
13744 SVN_WC__DB_WITH_TXN(
13745 wclock_obtain_cb(wcroot, local_relpath, levels_to_lock, steal_lock,
13748 return SVN_NO_ERROR;
13752 /* The body of svn_wc__db_wclock_find_root() and svn_wc__db_wclocked(). */
13753 static svn_error_t *
13754 find_wclock(const char **lock_relpath,
13755 svn_wc__db_wcroot_t *wcroot,
13756 const char *dir_relpath,
13757 apr_pool_t *result_pool,
13758 apr_pool_t *scratch_pool)
13760 svn_sqlite__stmt_t *stmt;
13761 svn_boolean_t have_row;
13762 int dir_depth = relpath_depth(dir_relpath);
13763 const char *first_relpath;
13765 /* Check for locks on all directories that might be ancestors.
13766 As our new apis only use recursive locks the number of locks stored
13767 in the DB will be very low */
13768 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13769 STMT_SELECT_ANCESTOR_WCLOCKS));
13771 /* Get the top level relpath to reduce the worst case number of results
13772 to the number of directories below this node plus two.
13773 (1: the node itself and 2: the wcroot). */
13774 first_relpath = strchr(dir_relpath, '/');
13776 if (first_relpath != NULL)
13777 first_relpath = apr_pstrndup(scratch_pool, dir_relpath,
13778 first_relpath - dir_relpath);
13780 first_relpath = dir_relpath;
13782 SVN_ERR(svn_sqlite__bindf(stmt, "iss",
13787 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13791 const char *relpath = svn_sqlite__column_text(stmt, 0, NULL);
13793 if (svn_relpath_skip_ancestor(relpath, dir_relpath))
13795 int locked_levels = svn_sqlite__column_int(stmt, 1);
13796 int row_depth = relpath_depth(relpath);
13798 if (locked_levels == -1
13799 || locked_levels + row_depth >= dir_depth)
13801 *lock_relpath = apr_pstrdup(result_pool, relpath);
13802 SVN_ERR(svn_sqlite__reset(stmt));
13803 return SVN_NO_ERROR;
13807 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13810 *lock_relpath = NULL;
13812 return svn_error_trace(svn_sqlite__reset(stmt));
13815 static svn_error_t *
13816 is_wclocked(svn_boolean_t *locked,
13817 svn_wc__db_wcroot_t *wcroot,
13818 const char *dir_relpath,
13819 apr_pool_t *scratch_pool)
13821 const char *lock_relpath;
13823 SVN_ERR(find_wclock(&lock_relpath, wcroot, dir_relpath,
13824 scratch_pool, scratch_pool));
13825 *locked = (lock_relpath != NULL);
13826 return SVN_NO_ERROR;
13831 svn_wc__db_wclock_find_root(const char **lock_abspath,
13833 const char *local_abspath,
13834 apr_pool_t *result_pool,
13835 apr_pool_t *scratch_pool)
13837 svn_wc__db_wcroot_t *wcroot;
13838 const char *local_relpath;
13839 const char *lock_relpath;
13841 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13842 local_abspath, scratch_pool, scratch_pool));
13843 VERIFY_USABLE_WCROOT(wcroot);
13845 SVN_WC__DB_WITH_TXN(
13846 find_wclock(&lock_relpath, wcroot, local_relpath,
13847 scratch_pool, scratch_pool),
13851 *lock_abspath = NULL;
13853 SVN_ERR(svn_wc__db_from_relpath(lock_abspath, db, wcroot->abspath,
13854 lock_relpath, result_pool, scratch_pool));
13855 return SVN_NO_ERROR;
13860 svn_wc__db_wclocked(svn_boolean_t *locked,
13862 const char *local_abspath,
13863 apr_pool_t *scratch_pool)
13865 svn_wc__db_wcroot_t *wcroot;
13866 const char *local_relpath;
13868 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13869 local_abspath, scratch_pool, scratch_pool));
13870 VERIFY_USABLE_WCROOT(wcroot);
13872 SVN_WC__DB_WITH_TXN(
13873 is_wclocked(locked, wcroot, local_relpath, scratch_pool),
13876 return SVN_NO_ERROR;
13881 svn_wc__db_wclock_release(svn_wc__db_t *db,
13882 const char *local_abspath,
13883 apr_pool_t *scratch_pool)
13885 svn_sqlite__stmt_t *stmt;
13886 svn_wc__db_wcroot_t *wcroot;
13887 const char *local_relpath;
13889 apr_array_header_t *owned_locks;
13891 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13892 local_abspath, scratch_pool, scratch_pool));
13894 VERIFY_USABLE_WCROOT(wcroot);
13896 /* First check and remove the owns-lock information as failure in
13897 removing the db record implies that we have to steal the lock later. */
13898 owned_locks = wcroot->owned_locks;
13899 for (i = 0; i < owned_locks->nelts; i++)
13901 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
13902 svn_wc__db_wclock_t);
13904 if (strcmp(lock->local_relpath, local_relpath) == 0)
13908 if (i >= owned_locks->nelts)
13909 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
13910 _("Working copy not locked at '%s'."),
13911 svn_dirent_local_style(local_abspath,
13914 if (i < owned_locks->nelts)
13916 owned_locks->nelts--;
13918 /* Move the last item in the array to the deleted place */
13919 if (owned_locks->nelts > 0)
13920 APR_ARRAY_IDX(owned_locks, i, svn_wc__db_wclock_t) =
13921 APR_ARRAY_IDX(owned_locks, owned_locks->nelts, svn_wc__db_wclock_t);
13924 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13925 STMT_DELETE_WC_LOCK));
13927 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13929 SVN_ERR(svn_sqlite__step_done(stmt));
13931 return SVN_NO_ERROR;
13935 /* Like svn_wc__db_wclock_owns_lock() but taking WCROOT+LOCAL_RELPATH instead
13936 of DB+LOCAL_ABSPATH. */
13937 static svn_error_t *
13938 wclock_owns_lock(svn_boolean_t *own_lock,
13939 svn_wc__db_wcroot_t *wcroot,
13940 const char *local_relpath,
13941 svn_boolean_t exact,
13942 apr_pool_t *scratch_pool)
13944 apr_array_header_t *owned_locks;
13949 owned_locks = wcroot->owned_locks;
13950 lock_level = relpath_depth(local_relpath);
13954 for (i = 0; i < owned_locks->nelts; i++)
13956 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
13957 svn_wc__db_wclock_t);
13959 if (strcmp(lock->local_relpath, local_relpath) == 0)
13962 return SVN_NO_ERROR;
13968 for (i = 0; i < owned_locks->nelts; i++)
13970 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
13971 svn_wc__db_wclock_t);
13973 if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
13974 && (lock->levels == -1
13975 || ((relpath_depth(lock->local_relpath) + lock->levels)
13979 return SVN_NO_ERROR;
13984 return SVN_NO_ERROR;
13989 svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
13991 const char *local_abspath,
13992 svn_boolean_t exact,
13993 apr_pool_t *scratch_pool)
13995 svn_wc__db_wcroot_t *wcroot;
13996 const char *local_relpath;
13998 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13999 local_abspath, scratch_pool, scratch_pool));
14002 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
14003 _("The node '%s' was not found."),
14004 svn_dirent_local_style(local_abspath,
14007 VERIFY_USABLE_WCROOT(wcroot);
14009 SVN_ERR(wclock_owns_lock(own_lock, wcroot, local_relpath, exact,
14012 return SVN_NO_ERROR;
14015 /* The body of svn_wc__db_temp_op_end_directory_update().
14017 static svn_error_t *
14018 end_directory_update(svn_wc__db_wcroot_t *wcroot,
14019 const char *local_relpath,
14020 apr_pool_t *scratch_pool)
14022 svn_sqlite__stmt_t *stmt;
14023 svn_wc__db_status_t base_status;
14025 SVN_ERR(svn_wc__db_base_get_info_internal(&base_status, NULL, NULL, NULL,
14026 NULL, NULL, NULL, NULL, NULL,
14027 NULL, NULL, NULL, NULL, NULL, NULL,
14028 wcroot, local_relpath,
14029 scratch_pool, scratch_pool));
14031 if (base_status == svn_wc__db_status_normal)
14032 return SVN_NO_ERROR;
14034 SVN_ERR_ASSERT(base_status == svn_wc__db_status_incomplete);
14036 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14037 STMT_UPDATE_NODE_BASE_PRESENCE));
14038 SVN_ERR(svn_sqlite__bindf(stmt, "ist", wcroot->wc_id, local_relpath,
14039 presence_map, svn_wc__db_status_normal));
14040 SVN_ERR(svn_sqlite__step_done(stmt));
14042 return SVN_NO_ERROR;
14046 svn_wc__db_temp_op_end_directory_update(svn_wc__db_t *db,
14047 const char *local_dir_abspath,
14048 apr_pool_t *scratch_pool)
14050 svn_wc__db_wcroot_t *wcroot;
14051 const char *local_relpath;
14053 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
14055 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14056 local_dir_abspath, scratch_pool, scratch_pool));
14057 VERIFY_USABLE_WCROOT(wcroot);
14059 SVN_WC__DB_WITH_TXN(
14060 end_directory_update(wcroot, local_relpath, scratch_pool),
14063 SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_empty,
14066 return SVN_NO_ERROR;
14070 /* The body of svn_wc__db_temp_op_start_directory_update().
14072 static svn_error_t *
14073 start_directory_update_txn(svn_wc__db_wcroot_t *wcroot,
14074 const char *local_relpath,
14075 const char *new_repos_relpath,
14076 svn_revnum_t new_rev,
14077 apr_pool_t *scratch_pool)
14079 svn_sqlite__stmt_t *stmt;
14081 /* Note: In the majority of calls, the repos_relpath is unchanged. */
14082 /* ### TODO: Maybe check if we can make repos_relpath NULL. */
14083 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14084 STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH));
14086 SVN_ERR(svn_sqlite__bindf(stmt, "istrs",
14089 presence_map, svn_wc__db_status_incomplete,
14091 new_repos_relpath));
14092 SVN_ERR(svn_sqlite__step_done(stmt));
14094 return SVN_NO_ERROR;
14099 svn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db,
14100 const char *local_abspath,
14101 const char *new_repos_relpath,
14102 svn_revnum_t new_rev,
14103 apr_pool_t *scratch_pool)
14105 svn_wc__db_wcroot_t *wcroot;
14106 const char *local_relpath;
14108 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14109 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_rev));
14110 SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
14112 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14113 local_abspath, scratch_pool, scratch_pool));
14114 VERIFY_USABLE_WCROOT(wcroot);
14116 SVN_WC__DB_WITH_TXN(
14117 start_directory_update_txn(wcroot, local_relpath,
14118 new_repos_relpath, new_rev, scratch_pool),
14121 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
14123 return SVN_NO_ERROR;
14127 /* The body of svn_wc__db_temp_op_make_copy(). This is
14128 used by the update editor when deleting a base node tree would be a
14129 tree-conflict because there are changes to subtrees. This function
14130 inserts a copy of the base node tree below any existing working
14131 subtrees. Given a tree:
14136 A/B normal - normal
14137 A/B/C normal - base-del normal
14138 A/F normal - normal
14139 A/F/G normal - normal
14140 A/F/H normal - base-deleted normal
14141 A/F/E normal - not-present
14145 This function adds layers to A and some of its descendants in an attempt
14146 to make the working copy look like as if it were a copy of the BASE nodes.
14151 A/B normal norm norm
14152 A/B/C normal norm base-del normal
14153 A/F normal norm norm
14154 A/F/G normal norm norm
14155 A/F/H normal norm not-pres
14156 A/F/E normal norm base-del
14158 A/X/Y incomplete incomplete
14160 static svn_error_t *
14161 make_copy_txn(svn_wc__db_wcroot_t *wcroot,
14162 const char *local_relpath,
14164 const svn_skel_t *conflicts,
14165 const svn_skel_t *work_items,
14166 apr_pool_t *scratch_pool)
14168 svn_sqlite__stmt_t *stmt;
14169 svn_boolean_t have_row;
14170 svn_boolean_t add_working_base_deleted = FALSE;
14171 svn_boolean_t remove_working = FALSE;
14172 const apr_array_header_t *children;
14173 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
14176 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14177 STMT_SELECT_LOWEST_WORKING_NODE));
14178 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
14179 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14183 svn_wc__db_status_t working_status;
14184 int working_op_depth;
14186 working_status = svn_sqlite__column_token(stmt, 1, presence_map);
14187 working_op_depth = svn_sqlite__column_int(stmt, 0);
14188 SVN_ERR(svn_sqlite__reset(stmt));
14190 SVN_ERR_ASSERT(working_status == svn_wc__db_status_normal
14191 || working_status == svn_wc__db_status_base_deleted
14192 || working_status == svn_wc__db_status_not_present
14193 || working_status == svn_wc__db_status_incomplete);
14195 /* Only change nodes in the layers where we are creating the copy.
14196 Deletes in higher layers will just apply to the copy */
14197 if (working_op_depth <= op_depth)
14199 add_working_base_deleted = TRUE;
14201 if (working_status == svn_wc__db_status_base_deleted)
14202 remove_working = TRUE;
14206 SVN_ERR(svn_sqlite__reset(stmt));
14208 if (remove_working)
14210 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14211 STMT_DELETE_LOWEST_WORKING_NODE));
14212 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14213 SVN_ERR(svn_sqlite__step_done(stmt));
14216 if (add_working_base_deleted)
14218 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14219 STMT_INSERT_DELETE_FROM_BASE));
14220 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14222 SVN_ERR(svn_sqlite__step_done(stmt));
14226 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14227 STMT_INSERT_WORKING_NODE_FROM_BASE_COPY));
14228 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14230 SVN_ERR(svn_sqlite__step_done(stmt));
14233 /* Get the BASE children, as WORKING children don't need modifications */
14234 SVN_ERR(gather_repo_children(&children, wcroot, local_relpath,
14235 0, scratch_pool, iterpool));
14237 for (i = 0; i < children->nelts; i++)
14239 const char *name = APR_ARRAY_IDX(children, i, const char *);
14240 const char *copy_relpath;
14242 svn_pool_clear(iterpool);
14244 copy_relpath = svn_relpath_join(local_relpath, name, iterpool);
14246 SVN_ERR(make_copy_txn(wcroot, copy_relpath, op_depth, NULL, NULL,
14250 SVN_ERR(flush_entries(wcroot, svn_dirent_join(wcroot->abspath, local_relpath,
14252 svn_depth_empty, iterpool));
14255 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
14256 conflicts, iterpool));
14258 SVN_ERR(add_work_items(wcroot->sdb, work_items, iterpool));
14260 svn_pool_destroy(iterpool);
14262 return SVN_NO_ERROR;
14267 svn_wc__db_op_make_copy(svn_wc__db_t *db,
14268 const char *local_abspath,
14269 const svn_skel_t *conflicts,
14270 const svn_skel_t *work_items,
14271 apr_pool_t *scratch_pool)
14273 svn_wc__db_wcroot_t *wcroot;
14274 const char *local_relpath;
14275 svn_sqlite__stmt_t *stmt;
14276 svn_boolean_t have_row;
14278 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14280 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14281 local_abspath, scratch_pool, scratch_pool));
14282 VERIFY_USABLE_WCROOT(wcroot);
14284 /* The update editor is supposed to call this function when there is
14285 no working node for LOCAL_ABSPATH. */
14286 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14287 STMT_SELECT_WORKING_NODE));
14288 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14289 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14290 SVN_ERR(svn_sqlite__reset(stmt));
14292 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
14293 _("Modification of '%s' already exists"),
14294 path_for_error_message(wcroot,
14298 /* We don't allow copies to contain server-excluded nodes;
14299 the update editor is going to have to bail out. */
14300 SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath, scratch_pool));
14302 SVN_WC__DB_WITH_TXN(
14303 make_copy_txn(wcroot, local_relpath,
14304 relpath_depth(local_relpath), conflicts, work_items,
14308 return SVN_NO_ERROR;
14312 svn_wc__db_info_below_working(svn_boolean_t *have_base,
14313 svn_boolean_t *have_work,
14314 svn_wc__db_status_t *status,
14316 const char *local_abspath,
14317 apr_pool_t *scratch_pool)
14319 svn_wc__db_wcroot_t *wcroot;
14320 const char *local_relpath;
14322 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14324 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14325 local_abspath, scratch_pool, scratch_pool));
14326 VERIFY_USABLE_WCROOT(wcroot);
14327 SVN_ERR(info_below_working(have_base, have_work, status,
14328 wcroot, local_relpath, -1, scratch_pool));
14330 return SVN_NO_ERROR;
14334 svn_wc__db_get_not_present_descendants(const apr_array_header_t **descendants,
14336 const char *local_abspath,
14337 apr_pool_t *result_pool,
14338 apr_pool_t *scratch_pool)
14340 svn_wc__db_wcroot_t *wcroot;
14341 const char *local_relpath;
14342 svn_sqlite__stmt_t *stmt;
14343 svn_boolean_t have_row;
14345 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14347 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14348 local_abspath, scratch_pool, scratch_pool));
14349 VERIFY_USABLE_WCROOT(wcroot);
14351 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14352 STMT_SELECT_NOT_PRESENT_DESCENDANTS));
14354 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
14357 relpath_depth(local_relpath)));
14359 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14363 apr_array_header_t *paths;
14365 paths = apr_array_make(result_pool, 4, sizeof(const char*));
14368 const char *found_relpath = svn_sqlite__column_text(stmt, 0, NULL);
14370 APR_ARRAY_PUSH(paths, const char *)
14371 = apr_pstrdup(result_pool, svn_relpath_skip_ancestor(
14372 local_relpath, found_relpath));
14374 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14377 *descendants = paths;
14380 *descendants = apr_array_make(result_pool, 0, sizeof(const char*));
14382 return svn_error_trace(svn_sqlite__reset(stmt));
14386 /* Like svn_wc__db_min_max_revisions(),
14387 * but accepts a WCROOT/LOCAL_RELPATH pair. */
14388 static svn_error_t *
14389 get_min_max_revisions(svn_revnum_t *min_revision,
14390 svn_revnum_t *max_revision,
14391 svn_wc__db_wcroot_t *wcroot,
14392 const char *local_relpath,
14393 svn_boolean_t committed,
14394 apr_pool_t *scratch_pool)
14396 svn_sqlite__stmt_t *stmt;
14397 svn_revnum_t min_rev, max_rev;
14399 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14400 STMT_SELECT_MIN_MAX_REVISIONS));
14401 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14402 SVN_ERR(svn_sqlite__step_row(stmt));
14406 min_rev = svn_sqlite__column_revnum(stmt, 2);
14407 max_rev = svn_sqlite__column_revnum(stmt, 3);
14411 min_rev = svn_sqlite__column_revnum(stmt, 0);
14412 max_rev = svn_sqlite__column_revnum(stmt, 1);
14415 /* The statement returns exactly one row. */
14416 SVN_ERR(svn_sqlite__reset(stmt));
14419 *min_revision = min_rev;
14421 *max_revision = max_rev;
14423 return SVN_NO_ERROR;
14428 svn_wc__db_min_max_revisions(svn_revnum_t *min_revision,
14429 svn_revnum_t *max_revision,
14431 const char *local_abspath,
14432 svn_boolean_t committed,
14433 apr_pool_t *scratch_pool)
14435 svn_wc__db_wcroot_t *wcroot;
14436 const char *local_relpath;
14438 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14440 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14442 scratch_pool, scratch_pool));
14443 VERIFY_USABLE_WCROOT(wcroot);
14445 return svn_error_trace(get_min_max_revisions(min_revision, max_revision,
14446 wcroot, local_relpath,
14447 committed, scratch_pool));
14451 /* Set *IS_SPARSE_CHECKOUT TRUE if LOCAL_RELPATH or any of the nodes
14452 * within LOCAL_RELPATH is sparse, FALSE otherwise. */
14453 static svn_error_t *
14454 is_sparse_checkout_internal(svn_boolean_t *is_sparse_checkout,
14455 svn_wc__db_wcroot_t *wcroot,
14456 const char *local_relpath,
14457 apr_pool_t *scratch_pool)
14459 svn_sqlite__stmt_t *stmt;
14460 svn_boolean_t have_row;
14462 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14463 STMT_HAS_SPARSE_NODES));
14464 SVN_ERR(svn_sqlite__bindf(stmt, "is",
14467 /* If this query returns a row, the working copy is sparse. */
14468 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14469 *is_sparse_checkout = have_row;
14470 SVN_ERR(svn_sqlite__reset(stmt));
14472 return SVN_NO_ERROR;
14476 /* Like svn_wc__db_has_switched_subtrees(),
14477 * but accepts a WCROOT/LOCAL_RELPATH pair. */
14478 static svn_error_t *
14479 has_switched_subtrees(svn_boolean_t *is_switched,
14480 svn_wc__db_wcroot_t *wcroot,
14481 const char *local_relpath,
14482 const char *trail_url,
14483 apr_pool_t *scratch_pool)
14485 svn_sqlite__stmt_t *stmt;
14486 svn_boolean_t have_row;
14487 apr_int64_t repos_id;
14488 const char *repos_relpath;
14490 /* Optional argument handling for caller */
14492 return SVN_NO_ERROR;
14494 *is_switched = FALSE;
14496 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
14497 &repos_relpath, &repos_id,
14498 NULL, NULL, NULL, NULL, NULL,
14499 NULL, NULL, NULL, NULL, NULL,
14500 wcroot, local_relpath,
14501 scratch_pool, scratch_pool));
14503 /* First do the cheap check where we only need info on the origin itself */
14504 if (trail_url != NULL)
14506 const char *repos_root_url;
14508 apr_size_t len1, len2;
14510 /* If the trailing part of the URL of the working copy directory
14511 does not match the given trailing URL then the whole working
14512 copy is switched. */
14514 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot->sdb,
14515 repos_id, scratch_pool));
14516 url = svn_path_url_add_component2(repos_root_url, repos_relpath,
14519 len1 = strlen(trail_url);
14520 len2 = strlen(url);
14521 if ((len1 > len2) || strcmp(url + len2 - len1, trail_url))
14523 *is_switched = TRUE;
14524 return SVN_NO_ERROR;
14528 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_HAS_SWITCHED));
14529 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath, repos_relpath));
14530 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14532 *is_switched = TRUE;
14533 SVN_ERR(svn_sqlite__reset(stmt));
14535 return SVN_NO_ERROR;
14540 svn_wc__db_has_switched_subtrees(svn_boolean_t *is_switched,
14542 const char *local_abspath,
14543 const char *trail_url,
14544 apr_pool_t *scratch_pool)
14546 svn_wc__db_wcroot_t *wcroot;
14547 const char *local_relpath;
14549 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14551 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14553 scratch_pool, scratch_pool));
14554 VERIFY_USABLE_WCROOT(wcroot);
14556 return svn_error_trace(has_switched_subtrees(is_switched, wcroot,
14557 local_relpath, trail_url,
14562 svn_wc__db_get_excluded_subtrees(apr_hash_t **excluded_subtrees,
14564 const char *local_abspath,
14565 apr_pool_t *result_pool,
14566 apr_pool_t *scratch_pool)
14568 svn_wc__db_wcroot_t *wcroot;
14569 const char *local_relpath;
14570 svn_sqlite__stmt_t *stmt;
14571 svn_boolean_t have_row;
14573 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14574 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14576 scratch_pool, scratch_pool));
14577 VERIFY_USABLE_WCROOT(wcroot);
14579 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14580 STMT_SELECT_ALL_EXCLUDED_DESCENDANTS));
14581 SVN_ERR(svn_sqlite__bindf(stmt, "is",
14584 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14587 *excluded_subtrees = apr_hash_make(result_pool);
14589 *excluded_subtrees = NULL;
14593 const char *abs_path =
14594 svn_dirent_join(wcroot->abspath,
14595 svn_sqlite__column_text(stmt, 0, NULL),
14597 svn_hash_sets(*excluded_subtrees, abs_path, abs_path);
14598 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14601 SVN_ERR(svn_sqlite__reset(stmt));
14602 return SVN_NO_ERROR;
14605 /* Like svn_wc__db_has_local_mods(),
14606 * but accepts a WCROOT/LOCAL_RELPATH pair.
14607 * ### This needs a DB as well as a WCROOT/RELPATH pair... */
14608 static svn_error_t *
14609 has_local_mods(svn_boolean_t *is_modified,
14610 svn_wc__db_wcroot_t *wcroot,
14611 const char *local_relpath,
14613 svn_cancel_func_t cancel_func,
14614 void *cancel_baton,
14615 apr_pool_t *scratch_pool)
14617 svn_sqlite__stmt_t *stmt;
14619 /* Check for additions or deletions. */
14620 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14621 STMT_SUBTREE_HAS_TREE_MODIFICATIONS));
14622 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14623 /* If this query returns a row, the working copy is modified. */
14624 SVN_ERR(svn_sqlite__step(is_modified, stmt));
14625 SVN_ERR(svn_sqlite__reset(stmt));
14628 SVN_ERR(cancel_func(cancel_baton));
14630 if (! *is_modified)
14632 /* Check for property modifications. */
14633 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14634 STMT_SUBTREE_HAS_PROP_MODIFICATIONS));
14635 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14636 /* If this query returns a row, the working copy is modified. */
14637 SVN_ERR(svn_sqlite__step(is_modified, stmt));
14638 SVN_ERR(svn_sqlite__reset(stmt));
14641 SVN_ERR(cancel_func(cancel_baton));
14644 if (! *is_modified)
14646 apr_pool_t *iterpool = NULL;
14647 svn_boolean_t have_row;
14649 /* Check for text modifications. */
14650 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14651 STMT_SELECT_BASE_FILES_RECURSIVE));
14652 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14653 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14655 iterpool = svn_pool_create(scratch_pool);
14658 const char *node_abspath;
14659 svn_filesize_t recorded_size;
14660 apr_time_t recorded_time;
14661 svn_boolean_t skip_check = FALSE;
14666 err = cancel_func(cancel_baton);
14668 return svn_error_trace(svn_error_compose_create(
14670 svn_sqlite__reset(stmt)));
14673 svn_pool_clear(iterpool);
14675 node_abspath = svn_dirent_join(wcroot->abspath,
14676 svn_sqlite__column_text(stmt, 0,
14680 recorded_size = get_recorded_size(stmt, 1);
14681 recorded_time = svn_sqlite__column_int64(stmt, 2);
14683 if (recorded_size != SVN_INVALID_FILESIZE
14684 && recorded_time != 0)
14686 const svn_io_dirent2_t *dirent;
14688 err = svn_io_stat_dirent2(&dirent, node_abspath, FALSE, TRUE,
14689 iterpool, iterpool);
14691 return svn_error_trace(svn_error_compose_create(
14693 svn_sqlite__reset(stmt)));
14695 if (dirent->kind != svn_node_file)
14697 *is_modified = TRUE; /* Missing or obstruction */
14700 else if (dirent->filesize == recorded_size
14701 && dirent->mtime == recorded_time)
14703 /* The file is not modified */
14710 err = svn_wc__internal_file_modified_p(is_modified,
14715 return svn_error_trace(svn_error_compose_create(
14717 svn_sqlite__reset(stmt)));
14723 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14726 svn_pool_destroy(iterpool);
14728 SVN_ERR(svn_sqlite__reset(stmt));
14731 return SVN_NO_ERROR;
14736 svn_wc__db_has_local_mods(svn_boolean_t *is_modified,
14738 const char *local_abspath,
14739 svn_cancel_func_t cancel_func,
14740 void *cancel_baton,
14741 apr_pool_t *scratch_pool)
14743 svn_wc__db_wcroot_t *wcroot;
14744 const char *local_relpath;
14746 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14748 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14750 scratch_pool, scratch_pool));
14751 VERIFY_USABLE_WCROOT(wcroot);
14753 return svn_error_trace(has_local_mods(is_modified, wcroot, local_relpath,
14754 db, cancel_func, cancel_baton,
14759 /* The body of svn_wc__db_revision_status().
14761 static svn_error_t *
14762 revision_status_txn(svn_revnum_t *min_revision,
14763 svn_revnum_t *max_revision,
14764 svn_boolean_t *is_sparse_checkout,
14765 svn_boolean_t *is_modified,
14766 svn_boolean_t *is_switched,
14767 svn_wc__db_wcroot_t *wcroot,
14768 const char *local_relpath,
14770 const char *trail_url,
14771 svn_boolean_t committed,
14772 svn_cancel_func_t cancel_func,
14773 void *cancel_baton,
14774 apr_pool_t *scratch_pool)
14777 svn_boolean_t exists;
14779 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
14783 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14784 _("The node '%s' was not found."),
14785 path_for_error_message(wcroot, local_relpath,
14789 /* Determine mixed-revisionness. */
14790 SVN_ERR(get_min_max_revisions(min_revision, max_revision, wcroot,
14791 local_relpath, committed, scratch_pool));
14794 SVN_ERR(cancel_func(cancel_baton));
14796 /* Determine sparseness. */
14797 SVN_ERR(is_sparse_checkout_internal(is_sparse_checkout, wcroot,
14798 local_relpath, scratch_pool));
14801 SVN_ERR(cancel_func(cancel_baton));
14803 /* Check for switched nodes. */
14805 err = has_switched_subtrees(is_switched, wcroot, local_relpath,
14806 trail_url, scratch_pool);
14810 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
14811 return svn_error_trace(err);
14813 svn_error_clear(err); /* No Base node, but no fatal error */
14814 *is_switched = FALSE;
14819 SVN_ERR(cancel_func(cancel_baton));
14821 /* Check for local mods. */
14822 SVN_ERR(has_local_mods(is_modified, wcroot, local_relpath, db,
14823 cancel_func, cancel_baton, scratch_pool));
14825 return SVN_NO_ERROR;
14830 svn_wc__db_revision_status(svn_revnum_t *min_revision,
14831 svn_revnum_t *max_revision,
14832 svn_boolean_t *is_sparse_checkout,
14833 svn_boolean_t *is_modified,
14834 svn_boolean_t *is_switched,
14836 const char *local_abspath,
14837 const char *trail_url,
14838 svn_boolean_t committed,
14839 svn_cancel_func_t cancel_func,
14840 void *cancel_baton,
14841 apr_pool_t *scratch_pool)
14843 svn_wc__db_wcroot_t *wcroot;
14844 const char *local_relpath;
14846 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14848 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14850 scratch_pool, scratch_pool));
14851 VERIFY_USABLE_WCROOT(wcroot);
14853 SVN_WC__DB_WITH_TXN(
14854 revision_status_txn(min_revision, max_revision,
14855 is_sparse_checkout, is_modified, is_switched,
14856 wcroot, local_relpath, db,
14857 trail_url, committed, cancel_func, cancel_baton,
14860 return SVN_NO_ERROR;
14865 svn_wc__db_base_get_lock_tokens_recursive(apr_hash_t **lock_tokens,
14867 const char *local_abspath,
14868 apr_pool_t *result_pool,
14869 apr_pool_t *scratch_pool)
14871 svn_wc__db_wcroot_t *wcroot;
14872 const char *local_relpath;
14873 svn_sqlite__stmt_t *stmt;
14874 svn_boolean_t have_row;
14875 apr_int64_t last_repos_id = INVALID_REPOS_ID;
14876 const char *last_repos_root_url = NULL;
14878 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14880 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14882 scratch_pool, scratch_pool));
14883 VERIFY_USABLE_WCROOT(wcroot);
14885 *lock_tokens = apr_hash_make(result_pool);
14887 /* Fetch all the lock tokens in and under LOCAL_RELPATH. */
14888 SVN_ERR(svn_sqlite__get_statement(
14889 &stmt, wcroot->sdb,
14890 STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE));
14892 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14893 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14896 apr_int64_t child_repos_id = svn_sqlite__column_int64(stmt, 0);
14897 const char *child_relpath = svn_sqlite__column_text(stmt, 1, NULL);
14898 const char *lock_token = svn_sqlite__column_text(stmt, 2, result_pool);
14900 if (child_repos_id != last_repos_id)
14902 svn_error_t *err = svn_wc__db_fetch_repos_info(&last_repos_root_url,
14909 return svn_error_trace(
14910 svn_error_compose_create(err,
14911 svn_sqlite__reset(stmt)));
14914 last_repos_id = child_repos_id;
14917 SVN_ERR_ASSERT(last_repos_root_url != NULL);
14918 svn_hash_sets(*lock_tokens,
14919 svn_path_url_add_component2(last_repos_root_url,
14920 child_relpath, result_pool),
14923 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14925 return svn_sqlite__reset(stmt);
14929 /* If EXPRESSION is false, cause the caller to return an SVN_ERR_WC_CORRUPT
14930 * error, showing EXPRESSION and the caller's LOCAL_RELPATH in the message. */
14931 #define VERIFY(expression) \
14933 if (! (expression)) \
14934 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, \
14935 _("database inconsistency at local_relpath='%s' verifying " \
14936 "expression '%s'"), local_relpath, #expression); \
14940 /* Verify consistency of the metadata concerning WCROOT. This is intended
14941 * for use only during testing and debugging, so is not intended to be
14944 * This code is a complement to any verification that we can do in SQLite
14945 * triggers. See, for example, 'wc-checks.sql'.
14947 * Some more verification steps we might want to add are:
14949 * * on every ACTUAL row (except root): a NODES row exists at its parent path
14950 * * the op-depth root must always exist and every intermediate too
14952 static svn_error_t *
14953 verify_wcroot(svn_wc__db_wcroot_t *wcroot,
14954 apr_pool_t *scratch_pool)
14956 svn_sqlite__stmt_t *stmt;
14957 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
14959 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14960 STMT_SELECT_ALL_NODES));
14961 SVN_ERR(svn_sqlite__bindf(stmt, "i", wcroot->wc_id));
14964 svn_boolean_t have_row;
14965 const char *local_relpath, *parent_relpath;
14968 svn_pool_clear(iterpool);
14970 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14974 op_depth = svn_sqlite__column_int(stmt, 0);
14975 local_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
14976 parent_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
14978 /* Verify parent_relpath is the parent path of local_relpath */
14979 VERIFY((parent_relpath == NULL)
14980 ? (local_relpath[0] == '\0')
14981 : (strcmp(svn_relpath_dirname(local_relpath, iterpool),
14982 parent_relpath) == 0));
14984 /* Verify op_depth <= the tree depth of local_relpath */
14985 VERIFY(op_depth <= relpath_depth(local_relpath));
14987 /* Verify parent_relpath refers to a row that exists */
14988 /* TODO: Verify there is a suitable parent row - e.g. has op_depth <=
14989 * the child's and a suitable presence */
14990 if (parent_relpath && svn_sqlite__column_is_null(stmt, 3))
14992 svn_sqlite__stmt_t *stmt2;
14993 svn_boolean_t have_a_parent_row;
14995 SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
14996 STMT_SELECT_NODE_INFO));
14997 SVN_ERR(svn_sqlite__bindf(stmt2, "is", wcroot->wc_id,
14999 SVN_ERR(svn_sqlite__step(&have_a_parent_row, stmt2));
15000 VERIFY(have_a_parent_row);
15001 SVN_ERR(svn_sqlite__reset(stmt2));
15004 svn_pool_destroy(iterpool);
15006 return svn_error_trace(svn_sqlite__reset(stmt));
15010 svn_wc__db_verify(svn_wc__db_t *db,
15011 const char *wri_abspath,
15012 apr_pool_t *scratch_pool)
15014 svn_wc__db_wcroot_t *wcroot;
15015 const char *local_relpath;
15017 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15019 scratch_pool, scratch_pool));
15020 VERIFY_USABLE_WCROOT(wcroot);
15022 SVN_ERR(verify_wcroot(wcroot, scratch_pool));
15023 return SVN_NO_ERROR;
15027 svn_wc__db_bump_format(int *result_format,
15028 svn_boolean_t *bumped_format,
15030 const char *wcroot_abspath,
15031 apr_pool_t *scratch_pool)
15033 svn_sqlite__db_t *sdb;
15038 *bumped_format = FALSE;
15040 /* Do not scan upwards for a working copy root here to prevent accidental
15041 * upgrades of any working copies the WCROOT might be nested in.
15042 * Just try to open a DB at the specified path instead. */
15043 err = svn_wc__db_util_open_db(&sdb, wcroot_abspath, SDB_FILE,
15044 svn_sqlite__mode_readwrite,
15045 TRUE, /* exclusive */
15046 NULL, /* my statements */
15047 scratch_pool, scratch_pool);
15051 apr_hash_t *entries;
15053 /* Could not open an sdb. Check for an entries file instead. */
15054 err2 = svn_wc__read_entries_old(&entries, wcroot_abspath,
15055 scratch_pool, scratch_pool);
15056 if (err2 || apr_hash_count(entries) == 0)
15057 return svn_error_createf(SVN_ERR_WC_INVALID_OP_ON_CWD,
15058 svn_error_compose_create(err, err2),
15059 _("Can't upgrade '%s' as it is not a working copy root"),
15060 svn_dirent_local_style(wcroot_abspath, scratch_pool));
15062 /* An entries file was found. This is a pre-wc-ng working copy
15063 * so suggest an upgrade. */
15064 return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, err,
15065 _("Working copy '%s' is too old and must be upgraded to "
15066 "at least format %d, as created by Subversion %s"),
15067 svn_dirent_local_style(wcroot_abspath, scratch_pool),
15068 SVN_WC__WC_NG_VERSION,
15069 svn_wc__version_string_from_format(SVN_WC__WC_NG_VERSION));
15072 SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool));
15073 err = svn_wc__upgrade_sdb(result_format, wcroot_abspath,
15074 sdb, format, scratch_pool);
15076 if (err == SVN_NO_ERROR && bumped_format)
15077 *bumped_format = (*result_format > format);
15079 /* Make sure we return a different error than expected for upgrades from
15081 if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
15082 err = svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, err,
15083 _("Working copy upgrade failed"));
15085 err = svn_error_compose_create(err, svn_sqlite__close(sdb));
15087 return svn_error_trace(err);
15091 svn_wc__db_vacuum(svn_wc__db_t *db,
15092 const char *local_abspath,
15093 apr_pool_t *scratch_pool)
15095 svn_wc__db_wcroot_t *wcroot;
15096 const char *local_relpath;
15098 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15100 scratch_pool, scratch_pool));
15101 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_VACUUM));
15103 return SVN_NO_ERROR;