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));
1393 /* Helper for create_db(). Initializes our wc.db schema.
1395 static svn_error_t *
1396 init_db(/* output values */
1397 apr_int64_t *repos_id,
1400 svn_sqlite__db_t *db,
1401 const char *repos_root_url,
1402 const char *repos_uuid,
1403 const char *root_node_repos_relpath,
1404 svn_revnum_t root_node_revision,
1405 svn_depth_t root_node_depth,
1406 apr_pool_t *scratch_pool)
1408 svn_sqlite__stmt_t *stmt;
1410 /* Create the database's schema. */
1411 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_SCHEMA));
1412 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES));
1413 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES_TRIGGERS));
1414 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_EXTERNALS));
1416 /* Insert the repository. */
1417 SVN_ERR(create_repos_id(repos_id, repos_root_url, repos_uuid,
1420 /* Insert the wcroot. */
1421 /* ### Right now, this just assumes wc metadata is being stored locally. */
1422 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT));
1423 SVN_ERR(svn_sqlite__insert(wc_id, stmt));
1425 if (root_node_repos_relpath)
1427 svn_wc__db_status_t status = svn_wc__db_status_normal;
1429 if (root_node_revision > 0)
1430 status = svn_wc__db_status_incomplete; /* Will be filled by update */
1432 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_NODE));
1433 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtst",
1436 0, /* op_depth is 0 for base */
1439 root_node_repos_relpath,
1441 presence_map, status, /* 8 */
1442 svn_token__to_word(depth_map,
1444 kind_map, svn_node_dir /* 10 */));
1446 SVN_ERR(svn_sqlite__insert(NULL, stmt));
1449 return SVN_NO_ERROR;
1452 /* Create an sqlite database at DIR_ABSPATH/SDB_FNAME and insert
1453 records for REPOS_ID (using REPOS_ROOT_URL and REPOS_UUID) into
1454 REPOSITORY and for WC_ID into WCROOT. Return the DB connection
1457 If ROOT_NODE_REPOS_RELPATH is not NULL, insert a BASE node at
1458 the working copy root with repository relpath ROOT_NODE_REPOS_RELPATH,
1459 revision ROOT_NODE_REVISION and depth ROOT_NODE_DEPTH.
1461 static svn_error_t *
1462 create_db(svn_sqlite__db_t **sdb,
1463 apr_int64_t *repos_id,
1465 const char *dir_abspath,
1466 const char *repos_root_url,
1467 const char *repos_uuid,
1468 const char *sdb_fname,
1469 const char *root_node_repos_relpath,
1470 svn_revnum_t root_node_revision,
1471 svn_depth_t root_node_depth,
1472 svn_boolean_t exclusive,
1473 apr_pool_t *result_pool,
1474 apr_pool_t *scratch_pool)
1476 SVN_ERR(svn_wc__db_util_open_db(sdb, dir_abspath, sdb_fname,
1477 svn_sqlite__mode_rwcreate, exclusive,
1478 NULL /* my_statements */,
1479 result_pool, scratch_pool));
1481 SVN_SQLITE__WITH_LOCK(init_db(repos_id, wc_id,
1482 *sdb, repos_root_url, repos_uuid,
1483 root_node_repos_relpath, root_node_revision,
1484 root_node_depth, scratch_pool),
1487 return SVN_NO_ERROR;
1492 svn_wc__db_init(svn_wc__db_t *db,
1493 const char *local_abspath,
1494 const char *repos_relpath,
1495 const char *repos_root_url,
1496 const char *repos_uuid,
1497 svn_revnum_t initial_rev,
1499 apr_pool_t *scratch_pool)
1501 svn_sqlite__db_t *sdb;
1502 apr_int64_t repos_id;
1504 svn_wc__db_wcroot_t *wcroot;
1505 svn_boolean_t sqlite_exclusive = FALSE;
1507 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1508 SVN_ERR_ASSERT(repos_relpath != NULL);
1509 SVN_ERR_ASSERT(depth == svn_depth_empty
1510 || depth == svn_depth_files
1511 || depth == svn_depth_immediates
1512 || depth == svn_depth_infinity);
1514 /* ### REPOS_ROOT_URL and REPOS_UUID may be NULL. ... more doc: tbd */
1516 SVN_ERR(svn_config_get_bool((svn_config_t *)db->config, &sqlite_exclusive,
1517 SVN_CONFIG_SECTION_WORKING_COPY,
1518 SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
1521 /* Create the SDB and insert the basic rows. */
1522 SVN_ERR(create_db(&sdb, &repos_id, &wc_id, local_abspath, repos_root_url,
1523 repos_uuid, SDB_FILE,
1524 repos_relpath, initial_rev, depth, sqlite_exclusive,
1525 db->state_pool, scratch_pool));
1527 /* Create the WCROOT for this directory. */
1528 SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
1529 apr_pstrdup(db->state_pool, local_abspath),
1530 sdb, wc_id, FORMAT_FROM_SDB,
1531 FALSE /* auto-upgrade */,
1532 FALSE /* enforce_empty_wq */,
1533 db->state_pool, scratch_pool));
1535 /* The WCROOT is complete. Stash it into DB. */
1536 svn_hash_sets(db->dir_data, wcroot->abspath, wcroot);
1538 return SVN_NO_ERROR;
1543 svn_wc__db_to_relpath(const char **local_relpath,
1545 const char *wri_abspath,
1546 const char *local_abspath,
1547 apr_pool_t *result_pool,
1548 apr_pool_t *scratch_pool)
1550 svn_wc__db_wcroot_t *wcroot;
1551 const char *relpath;
1553 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1555 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &relpath, db,
1556 wri_abspath, result_pool, scratch_pool));
1558 /* This function is indirectly called from the upgrade code, so we
1559 can't verify the wcroot here. Just check that it is not NULL */
1560 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1562 if (svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
1564 *local_relpath = apr_pstrdup(result_pool,
1565 svn_dirent_skip_ancestor(wcroot->abspath,
1569 /* Probably moving from $TMP. Should we allow this? */
1570 *local_relpath = apr_pstrdup(result_pool, local_abspath);
1572 return SVN_NO_ERROR;
1577 svn_wc__db_from_relpath(const char **local_abspath,
1579 const char *wri_abspath,
1580 const char *local_relpath,
1581 apr_pool_t *result_pool,
1582 apr_pool_t *scratch_pool)
1584 svn_wc__db_wcroot_t *wcroot;
1585 const char *unused_relpath;
1587 SVN_ERR_ASSERT(svn_relpath_is_canonical(local_relpath));
1590 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1591 wri_abspath, scratch_pool, scratch_pool));
1593 /* This function is indirectly called from the upgrade code, so we
1594 can't verify the wcroot here. Just check that it is not NULL */
1595 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1598 *local_abspath = svn_dirent_join(wcroot->abspath,
1601 return SVN_NO_ERROR;
1606 svn_wc__db_get_wcroot(const char **wcroot_abspath,
1608 const char *wri_abspath,
1609 apr_pool_t *result_pool,
1610 apr_pool_t *scratch_pool)
1612 svn_wc__db_wcroot_t *wcroot;
1613 const char *unused_relpath;
1615 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1616 wri_abspath, scratch_pool, scratch_pool));
1618 /* Can't use VERIFY_USABLE_WCROOT, as this should be usable to detect
1619 where call upgrade */
1620 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1622 *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
1624 return SVN_NO_ERROR;
1629 svn_wc__db_base_add_directory(svn_wc__db_t *db,
1630 const char *local_abspath,
1631 const char *wri_abspath,
1632 const char *repos_relpath,
1633 const char *repos_root_url,
1634 const char *repos_uuid,
1635 svn_revnum_t revision,
1636 const apr_hash_t *props,
1637 svn_revnum_t changed_rev,
1638 apr_time_t changed_date,
1639 const char *changed_author,
1640 const apr_array_header_t *children,
1642 apr_hash_t *dav_cache,
1643 const svn_skel_t *conflict,
1644 svn_boolean_t update_actual_props,
1645 apr_hash_t *new_actual_props,
1646 apr_array_header_t *new_iprops,
1647 const svn_skel_t *work_items,
1648 apr_pool_t *scratch_pool)
1650 svn_wc__db_wcroot_t *wcroot;
1651 const char *local_relpath;
1652 insert_base_baton_t ibb;
1654 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1655 SVN_ERR_ASSERT(repos_relpath != NULL);
1656 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1657 SVN_ERR_ASSERT(repos_uuid != NULL);
1658 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1659 SVN_ERR_ASSERT(props != NULL);
1660 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1662 SVN_ERR_ASSERT(children != NULL);
1665 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1666 wri_abspath, scratch_pool, scratch_pool));
1667 VERIFY_USABLE_WCROOT(wcroot);
1668 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1672 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1673 ibb.repos_root_url = repos_root_url;
1674 ibb.repos_uuid = repos_uuid;
1676 ibb.status = svn_wc__db_status_normal;
1677 ibb.kind = svn_node_dir;
1678 ibb.repos_relpath = repos_relpath;
1679 ibb.revision = revision;
1681 ibb.iprops = new_iprops;
1683 ibb.changed_rev = changed_rev;
1684 ibb.changed_date = changed_date;
1685 ibb.changed_author = changed_author;
1687 ibb.children = children;
1690 ibb.dav_cache = dav_cache;
1691 ibb.conflict = conflict;
1692 ibb.work_items = work_items;
1694 if (update_actual_props)
1696 ibb.update_actual_props = TRUE;
1697 ibb.new_actual_props = new_actual_props;
1700 /* Insert the directory and all its children transactionally.
1702 Note: old children can stick around, even if they are no longer present
1703 in this directory's revision. */
1704 SVN_WC__DB_WITH_TXN(
1705 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1708 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
1709 return SVN_NO_ERROR;
1713 svn_wc__db_base_add_incomplete_directory(svn_wc__db_t *db,
1714 const char *local_abspath,
1715 const char *repos_relpath,
1716 const char *repos_root_url,
1717 const char *repos_uuid,
1718 svn_revnum_t revision,
1720 svn_boolean_t insert_base_deleted,
1721 svn_boolean_t delete_working,
1722 svn_skel_t *conflict,
1723 svn_skel_t *work_items,
1724 apr_pool_t *scratch_pool)
1726 svn_wc__db_wcroot_t *wcroot;
1727 const char *local_relpath;
1728 struct insert_base_baton_t ibb;
1730 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1731 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1732 SVN_ERR_ASSERT(repos_relpath && repos_root_url && repos_uuid);
1734 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
1736 scratch_pool, scratch_pool));
1738 VERIFY_USABLE_WCROOT(wcroot);
1742 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1743 ibb.repos_root_url = repos_root_url;
1744 ibb.repos_uuid = repos_uuid;
1746 ibb.status = svn_wc__db_status_incomplete;
1747 ibb.kind = svn_node_dir;
1748 ibb.repos_relpath = repos_relpath;
1749 ibb.revision = revision;
1751 ibb.insert_base_deleted = insert_base_deleted;
1752 ibb.delete_working = delete_working;
1754 ibb.conflict = conflict;
1755 ibb.work_items = work_items;
1757 SVN_WC__DB_WITH_TXN(
1758 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1761 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
1763 return SVN_NO_ERROR;
1768 svn_wc__db_base_add_file(svn_wc__db_t *db,
1769 const char *local_abspath,
1770 const char *wri_abspath,
1771 const char *repos_relpath,
1772 const char *repos_root_url,
1773 const char *repos_uuid,
1774 svn_revnum_t revision,
1775 const apr_hash_t *props,
1776 svn_revnum_t changed_rev,
1777 apr_time_t changed_date,
1778 const char *changed_author,
1779 const svn_checksum_t *checksum,
1780 apr_hash_t *dav_cache,
1781 svn_boolean_t delete_working,
1782 svn_boolean_t update_actual_props,
1783 apr_hash_t *new_actual_props,
1784 apr_array_header_t *new_iprops,
1785 svn_boolean_t keep_recorded_info,
1786 svn_boolean_t insert_base_deleted,
1787 const svn_skel_t *conflict,
1788 const svn_skel_t *work_items,
1789 apr_pool_t *scratch_pool)
1791 svn_wc__db_wcroot_t *wcroot;
1792 const char *local_relpath;
1793 insert_base_baton_t ibb;
1795 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1796 SVN_ERR_ASSERT(repos_relpath != NULL);
1797 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1798 SVN_ERR_ASSERT(repos_uuid != NULL);
1799 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1800 SVN_ERR_ASSERT(props != NULL);
1801 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1802 SVN_ERR_ASSERT(checksum != NULL);
1804 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1805 wri_abspath, scratch_pool, scratch_pool));
1806 VERIFY_USABLE_WCROOT(wcroot);
1807 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1811 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1812 ibb.repos_root_url = repos_root_url;
1813 ibb.repos_uuid = repos_uuid;
1815 ibb.status = svn_wc__db_status_normal;
1816 ibb.kind = svn_node_file;
1817 ibb.repos_relpath = repos_relpath;
1818 ibb.revision = revision;
1821 ibb.changed_rev = changed_rev;
1822 ibb.changed_date = changed_date;
1823 ibb.changed_author = changed_author;
1825 ibb.checksum = checksum;
1827 ibb.dav_cache = dav_cache;
1828 ibb.iprops = new_iprops;
1830 if (update_actual_props)
1832 ibb.update_actual_props = TRUE;
1833 ibb.new_actual_props = new_actual_props;
1836 ibb.keep_recorded_info = keep_recorded_info;
1837 ibb.insert_base_deleted = insert_base_deleted;
1838 ibb.delete_working = delete_working;
1840 ibb.conflict = conflict;
1841 ibb.work_items = work_items;
1843 SVN_WC__DB_WITH_TXN(
1844 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1847 /* If this used to be a directory we should remove children so pass
1848 * depth infinity. */
1849 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1851 return SVN_NO_ERROR;
1856 svn_wc__db_base_add_symlink(svn_wc__db_t *db,
1857 const char *local_abspath,
1858 const char *wri_abspath,
1859 const char *repos_relpath,
1860 const char *repos_root_url,
1861 const char *repos_uuid,
1862 svn_revnum_t revision,
1863 const apr_hash_t *props,
1864 svn_revnum_t changed_rev,
1865 apr_time_t changed_date,
1866 const char *changed_author,
1868 apr_hash_t *dav_cache,
1869 svn_boolean_t delete_working,
1870 svn_boolean_t update_actual_props,
1871 apr_hash_t *new_actual_props,
1872 apr_array_header_t *new_iprops,
1873 svn_boolean_t keep_recorded_info,
1874 svn_boolean_t insert_base_deleted,
1875 const svn_skel_t *conflict,
1876 const svn_skel_t *work_items,
1877 apr_pool_t *scratch_pool)
1879 svn_wc__db_wcroot_t *wcroot;
1880 const char *local_relpath;
1881 insert_base_baton_t ibb;
1883 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1884 SVN_ERR_ASSERT(repos_relpath != NULL);
1885 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1886 SVN_ERR_ASSERT(repos_uuid != NULL);
1887 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1888 SVN_ERR_ASSERT(props != NULL);
1889 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1890 SVN_ERR_ASSERT(target != NULL);
1892 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1893 wri_abspath, scratch_pool, scratch_pool));
1894 VERIFY_USABLE_WCROOT(wcroot);
1895 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1898 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1899 ibb.repos_root_url = repos_root_url;
1900 ibb.repos_uuid = repos_uuid;
1902 ibb.status = svn_wc__db_status_normal;
1903 ibb.kind = svn_node_symlink;
1904 ibb.repos_relpath = repos_relpath;
1905 ibb.revision = revision;
1908 ibb.changed_rev = changed_rev;
1909 ibb.changed_date = changed_date;
1910 ibb.changed_author = changed_author;
1912 ibb.target = target;
1914 ibb.dav_cache = dav_cache;
1915 ibb.iprops = new_iprops;
1917 if (update_actual_props)
1919 ibb.update_actual_props = TRUE;
1920 ibb.new_actual_props = new_actual_props;
1923 ibb.keep_recorded_info = keep_recorded_info;
1924 ibb.insert_base_deleted = insert_base_deleted;
1925 ibb.delete_working = delete_working;
1927 ibb.conflict = conflict;
1928 ibb.work_items = work_items;
1930 SVN_WC__DB_WITH_TXN(
1931 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1934 /* If this used to be a directory we should remove children so pass
1935 * depth infinity. */
1936 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1938 return SVN_NO_ERROR;
1942 static svn_error_t *
1943 add_excluded_or_not_present_node(svn_wc__db_t *db,
1944 const char *local_abspath,
1945 const char *repos_relpath,
1946 const char *repos_root_url,
1947 const char *repos_uuid,
1948 svn_revnum_t revision,
1949 svn_node_kind_t kind,
1950 svn_wc__db_status_t status,
1951 const svn_skel_t *conflict,
1952 const svn_skel_t *work_items,
1953 apr_pool_t *scratch_pool)
1955 svn_wc__db_wcroot_t *wcroot;
1956 const char *local_relpath;
1957 insert_base_baton_t ibb;
1958 const char *dir_abspath, *name;
1960 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1961 SVN_ERR_ASSERT(repos_relpath != NULL);
1962 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1963 SVN_ERR_ASSERT(repos_uuid != NULL);
1964 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1965 SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
1966 || status == svn_wc__db_status_excluded
1967 || status == svn_wc__db_status_not_present);
1969 /* These absent presence nodes are only useful below a parent node that is
1970 present. To avoid problems with working copies obstructing the child
1971 we calculate the wcroot and local_relpath of the parent and then add
1974 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
1976 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1977 dir_abspath, scratch_pool, scratch_pool));
1978 VERIFY_USABLE_WCROOT(wcroot);
1980 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
1984 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1985 ibb.repos_root_url = repos_root_url;
1986 ibb.repos_uuid = repos_uuid;
1988 ibb.status = status;
1990 ibb.repos_relpath = repos_relpath;
1991 ibb.revision = revision;
1993 /* Depending upon KIND, any of these might get used. */
1994 ibb.children = NULL;
1995 ibb.depth = svn_depth_unknown;
1996 ibb.checksum = NULL;
1999 ibb.conflict = conflict;
2000 ibb.work_items = work_items;
2002 SVN_WC__DB_WITH_TXN(
2003 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
2006 /* If this used to be a directory we should remove children so pass
2007 * depth infinity. */
2008 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2011 return SVN_NO_ERROR;
2016 svn_wc__db_base_add_excluded_node(svn_wc__db_t *db,
2017 const char *local_abspath,
2018 const char *repos_relpath,
2019 const char *repos_root_url,
2020 const char *repos_uuid,
2021 svn_revnum_t revision,
2022 svn_node_kind_t kind,
2023 svn_wc__db_status_t status,
2024 const svn_skel_t *conflict,
2025 const svn_skel_t *work_items,
2026 apr_pool_t *scratch_pool)
2028 SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
2029 || status == svn_wc__db_status_excluded);
2031 return add_excluded_or_not_present_node(
2032 db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2033 kind, status, conflict, work_items, scratch_pool);
2038 svn_wc__db_base_add_not_present_node(svn_wc__db_t *db,
2039 const char *local_abspath,
2040 const char *repos_relpath,
2041 const char *repos_root_url,
2042 const char *repos_uuid,
2043 svn_revnum_t revision,
2044 svn_node_kind_t kind,
2045 const svn_skel_t *conflict,
2046 const svn_skel_t *work_items,
2047 apr_pool_t *scratch_pool)
2049 return add_excluded_or_not_present_node(
2050 db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2051 kind, svn_wc__db_status_not_present, conflict, work_items, scratch_pool);
2054 /* Recursively clear moved-here information at the copy-half of the move
2055 * which moved the node at SRC_RELPATH away. This transforms the move into
2057 static svn_error_t *
2058 clear_moved_here(const char *src_relpath,
2059 svn_wc__db_wcroot_t *wcroot,
2060 apr_pool_t *scratch_pool)
2062 svn_sqlite__stmt_t *stmt;
2063 const char *dst_relpath;
2065 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
2066 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2067 src_relpath, relpath_depth(src_relpath)));
2068 SVN_ERR(svn_sqlite__step_row(stmt));
2069 dst_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
2070 SVN_ERR(svn_sqlite__reset(stmt));
2072 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2073 STMT_CLEAR_MOVED_HERE_RECURSIVE));
2074 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2075 dst_relpath, relpath_depth(dst_relpath)));
2076 SVN_ERR(svn_sqlite__step_done(stmt));
2078 return SVN_NO_ERROR;
2081 /* The body of svn_wc__db_base_remove().
2083 static svn_error_t *
2084 db_base_remove(svn_wc__db_wcroot_t *wcroot,
2085 const char *local_relpath,
2086 svn_wc__db_t *db, /* For checking conflicts */
2087 svn_boolean_t keep_as_working,
2088 svn_boolean_t queue_deletes,
2089 svn_boolean_t remove_locks,
2090 svn_revnum_t not_present_revision,
2091 svn_skel_t *conflict,
2092 svn_skel_t *work_items,
2093 apr_pool_t *scratch_pool)
2095 svn_sqlite__stmt_t *stmt;
2096 svn_boolean_t have_row;
2097 svn_wc__db_status_t status;
2098 apr_int64_t repos_id;
2099 const char *repos_relpath;
2100 svn_node_kind_t kind;
2101 svn_boolean_t keep_working;
2103 SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, NULL,
2104 &repos_relpath, &repos_id,
2105 NULL, NULL, NULL, NULL, NULL,
2106 NULL, NULL, NULL, NULL, NULL,
2107 wcroot, local_relpath,
2108 scratch_pool, scratch_pool));
2112 svn_sqlite__stmt_t *lock_stmt;
2114 SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
2115 STMT_DELETE_LOCK_RECURSIVELY));
2116 SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
2117 SVN_ERR(svn_sqlite__step_done(lock_stmt));
2120 if (status == svn_wc__db_status_normal
2123 SVN_ERR(svn_wc__db_op_make_copy(db,
2124 svn_dirent_join(wcroot->abspath,
2129 keep_working = TRUE;
2133 /* Check if there is already a working node */
2134 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2135 STMT_SELECT_WORKING_NODE));
2136 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2137 SVN_ERR(svn_sqlite__step(&keep_working, stmt));
2138 SVN_ERR(svn_sqlite__reset(stmt));
2141 /* Step 1: Create workqueue operations to remove files and dirs in the
2145 && (status == svn_wc__db_status_normal
2146 || status == svn_wc__db_status_incomplete))
2148 svn_skel_t *work_item;
2149 const char *local_abspath;
2151 local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
2153 if (kind == svn_node_dir)
2155 apr_pool_t *iterpool;
2156 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2157 STMT_SELECT_BASE_PRESENT));
2158 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2160 iterpool = svn_pool_create(scratch_pool);
2162 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2166 const char *node_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2167 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 1,
2169 const char *node_abspath;
2172 svn_pool_clear(iterpool);
2174 node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
2177 if (node_kind == svn_node_dir)
2178 err = svn_wc__wq_build_dir_remove(&work_item,
2179 db, wcroot->abspath,
2180 node_abspath, FALSE,
2181 iterpool, iterpool);
2183 err = svn_wc__wq_build_file_remove(&work_item,
2187 iterpool, iterpool);
2190 err = add_work_items(wcroot->sdb, work_item, iterpool);
2192 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2194 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2197 SVN_ERR(svn_sqlite__reset(stmt));
2199 SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
2200 db, wcroot->abspath,
2201 local_abspath, FALSE,
2202 scratch_pool, iterpool));
2203 svn_pool_destroy(iterpool);
2206 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
2207 db, wcroot->abspath,
2209 scratch_pool, scratch_pool));
2211 SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
2214 /* Step 2: Delete ACTUAL nodes */
2217 /* There won't be a record in NODE left for this node, so we want
2218 to remove *all* ACTUAL nodes, including ACTUAL ONLY. */
2219 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2220 STMT_DELETE_ACTUAL_NODE_RECURSIVE));
2221 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2222 SVN_ERR(svn_sqlite__step_done(stmt));
2224 else if (! keep_as_working)
2226 /* Delete only the ACTUAL nodes that apply to a delete of a BASE node */
2227 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2228 STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE));
2229 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2230 SVN_ERR(svn_sqlite__step_done(stmt));
2232 /* Else: Everything has been turned into a copy, so we want to keep all
2233 ACTUAL_NODE records */
2235 /* Step 3: Delete WORKING nodes */
2238 apr_pool_t *iterpool;
2241 * When deleting a conflicted node, moves of any moved-outside children
2242 * of the node must be broken. Else, the destination will still be marked
2243 * moved-here after the move source disappears from the working copy.
2245 * ### FIXME: It would be nicer to have the conflict resolver
2246 * break the move instead. It might also be a good idea to
2247 * flag a tree conflict on each moved-away child. But doing so
2248 * might introduce actual-only nodes without direct parents,
2249 * and we're not yet sure if other existing code is prepared
2250 * to handle such nodes. To be revisited post-1.8.
2252 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2253 STMT_SELECT_MOVED_OUTSIDE));
2254 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2256 relpath_depth(local_relpath)));
2257 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2258 iterpool = svn_pool_create(scratch_pool);
2261 const char *child_relpath;
2264 svn_pool_clear(iterpool);
2265 child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
2266 err = clear_moved_here(child_relpath, wcroot, iterpool);
2268 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2269 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2271 svn_pool_destroy(iterpool);
2272 SVN_ERR(svn_sqlite__reset(stmt));
2276 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2277 STMT_DELETE_WORKING_BASE_DELETE));
2278 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2279 SVN_ERR(svn_sqlite__step_done(stmt));
2283 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2284 STMT_DELETE_WORKING_RECURSIVE));
2285 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2286 SVN_ERR(svn_sqlite__step_done(stmt));
2289 /* Step 4: Delete the BASE node descendants */
2290 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2291 STMT_DELETE_BASE_RECURSIVE));
2292 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2293 SVN_ERR(svn_sqlite__step_done(stmt));
2295 /* Step 5: handle the BASE node itself */
2296 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2297 STMT_DELETE_BASE_NODE));
2298 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2299 SVN_ERR(svn_sqlite__step_done(stmt));
2301 SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
2304 /* Step 6: Delete actual node if we don't keep working */
2307 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2308 STMT_DELETE_ACTUAL_NODE));
2309 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2310 SVN_ERR(svn_sqlite__step_done(stmt));
2313 if (SVN_IS_VALID_REVNUM(not_present_revision))
2315 struct insert_base_baton_t ibb;
2318 ibb.repos_id = repos_id;
2319 ibb.status = svn_wc__db_status_not_present;
2321 ibb.repos_relpath = repos_relpath;
2322 ibb.revision = not_present_revision;
2324 /* Depending upon KIND, any of these might get used. */
2325 ibb.children = NULL;
2326 ibb.depth = svn_depth_unknown;
2327 ibb.checksum = NULL;
2330 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
2333 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2335 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
2336 conflict, scratch_pool));
2338 return SVN_NO_ERROR;
2343 svn_wc__db_base_remove(svn_wc__db_t *db,
2344 const char *local_abspath,
2345 svn_boolean_t keep_as_working,
2346 svn_boolean_t queue_deletes,
2347 svn_boolean_t remove_locks,
2348 svn_revnum_t not_present_revision,
2349 svn_skel_t *conflict,
2350 svn_skel_t *work_items,
2351 apr_pool_t *scratch_pool)
2353 svn_wc__db_wcroot_t *wcroot;
2354 const char *local_relpath;
2356 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2358 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2359 local_abspath, scratch_pool, scratch_pool));
2360 VERIFY_USABLE_WCROOT(wcroot);
2362 SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath,
2363 db, keep_as_working, queue_deletes,
2364 remove_locks, not_present_revision,
2365 conflict, work_items, scratch_pool),
2368 /* If this used to be a directory we should remove children so pass
2369 * depth infinity. */
2370 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2373 return SVN_NO_ERROR;
2378 svn_wc__db_base_get_info_internal(svn_wc__db_status_t *status,
2379 svn_node_kind_t *kind,
2380 svn_revnum_t *revision,
2381 const char **repos_relpath,
2382 apr_int64_t *repos_id,
2383 svn_revnum_t *changed_rev,
2384 apr_time_t *changed_date,
2385 const char **changed_author,
2387 const svn_checksum_t **checksum,
2388 const char **target,
2389 svn_wc__db_lock_t **lock,
2390 svn_boolean_t *had_props,
2392 svn_boolean_t *update_root,
2393 svn_wc__db_wcroot_t *wcroot,
2394 const char *local_relpath,
2395 apr_pool_t *result_pool,
2396 apr_pool_t *scratch_pool)
2398 svn_sqlite__stmt_t *stmt;
2399 svn_boolean_t have_row;
2400 svn_error_t *err = SVN_NO_ERROR;
2402 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2403 lock ? STMT_SELECT_BASE_NODE_WITH_LOCK
2404 : STMT_SELECT_BASE_NODE));
2405 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2406 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2410 svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2412 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2420 *status = node_status;
2422 repos_location_from_columns(repos_id, revision, repos_relpath,
2423 stmt, 0, 4, 1, result_pool);
2424 SVN_ERR_ASSERT(!repos_id || *repos_id != INVALID_REPOS_ID);
2425 SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
2428 *lock = lock_from_columns(stmt, 15, 16, 17, 18, result_pool);
2432 *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2436 *changed_date = svn_sqlite__column_int64(stmt, 8);
2440 /* Result may be NULL. */
2441 *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2445 if (node_kind != svn_node_dir)
2447 *depth = svn_depth_unknown;
2451 *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2457 if (node_kind != svn_node_file)
2463 err = svn_sqlite__column_checksum(checksum, stmt, 5,
2466 err = svn_error_createf(
2468 _("The node '%s' has a corrupt checksum value."),
2469 path_for_error_message(wcroot, local_relpath,
2475 if (node_kind != svn_node_symlink)
2478 *target = svn_sqlite__column_text(stmt, 11, result_pool);
2482 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2486 if (node_status == svn_wc__db_status_normal
2487 || node_status == svn_wc__db_status_incomplete)
2489 SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2490 result_pool, scratch_pool));
2492 *props = apr_hash_make(result_pool);
2496 assert(svn_sqlite__column_is_null(stmt, 13));
2502 /* It's an update root iff it's a file external. */
2503 *update_root = svn_sqlite__column_boolean(stmt, 14);
2508 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2509 _("The node '%s' was not found."),
2510 path_for_error_message(wcroot, local_relpath,
2514 /* Note: given the composition, no need to wrap for tracing. */
2515 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2520 svn_wc__db_base_get_info(svn_wc__db_status_t *status,
2521 svn_node_kind_t *kind,
2522 svn_revnum_t *revision,
2523 const char **repos_relpath,
2524 const char **repos_root_url,
2525 const char **repos_uuid,
2526 svn_revnum_t *changed_rev,
2527 apr_time_t *changed_date,
2528 const char **changed_author,
2530 const svn_checksum_t **checksum,
2531 const char **target,
2532 svn_wc__db_lock_t **lock,
2533 svn_boolean_t *had_props,
2535 svn_boolean_t *update_root,
2537 const char *local_abspath,
2538 apr_pool_t *result_pool,
2539 apr_pool_t *scratch_pool)
2541 svn_wc__db_wcroot_t *wcroot;
2542 const char *local_relpath;
2543 apr_int64_t repos_id;
2545 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2547 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2548 local_abspath, scratch_pool, scratch_pool));
2549 VERIFY_USABLE_WCROOT(wcroot);
2551 SVN_ERR(svn_wc__db_base_get_info_internal(status, kind, revision,
2552 repos_relpath, &repos_id,
2553 changed_rev, changed_date,
2554 changed_author, depth,
2555 checksum, target, lock,
2556 had_props, props, update_root,
2557 wcroot, local_relpath,
2558 result_pool, scratch_pool));
2559 SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
2560 SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
2561 wcroot->sdb, repos_id, result_pool));
2563 return SVN_NO_ERROR;
2567 svn_wc__db_base_get_children_info(apr_hash_t **nodes,
2569 const char *dir_abspath,
2570 apr_pool_t *result_pool,
2571 apr_pool_t *scratch_pool)
2573 svn_wc__db_wcroot_t *wcroot;
2574 const char *local_relpath;
2575 svn_sqlite__stmt_t *stmt;
2576 svn_boolean_t have_row;
2578 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
2580 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2581 dir_abspath, scratch_pool, scratch_pool));
2582 VERIFY_USABLE_WCROOT(wcroot);
2584 *nodes = apr_hash_make(result_pool);
2586 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2587 STMT_SELECT_BASE_CHILDREN_INFO));
2588 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2590 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2594 struct svn_wc__db_base_info_t *info;
2596 apr_int64_t repos_id;
2597 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2598 const char *name = svn_relpath_basename(child_relpath, result_pool);
2600 info = apr_pcalloc(result_pool, sizeof(*info));
2602 repos_id = svn_sqlite__column_int64(stmt, 1);
2603 info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
2604 info->status = svn_sqlite__column_token(stmt, 3, presence_map);
2605 info->kind = svn_sqlite__column_token(stmt, 4, kind_map);
2606 info->revnum = svn_sqlite__column_revnum(stmt, 5);
2608 info->depth = svn_sqlite__column_token_null(stmt, 6, depth_map,
2611 info->update_root = svn_sqlite__column_boolean(stmt, 7);
2613 info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool);
2615 err = svn_wc__db_fetch_repos_info(&info->repos_root_url, NULL,
2616 wcroot->sdb, repos_id, result_pool);
2619 return svn_error_trace(
2620 svn_error_compose_create(err,
2621 svn_sqlite__reset(stmt)));
2624 svn_hash_sets(*nodes, name, info);
2626 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2629 SVN_ERR(svn_sqlite__reset(stmt));
2631 return SVN_NO_ERROR;
2636 svn_wc__db_base_get_props(apr_hash_t **props,
2638 const char *local_abspath,
2639 apr_pool_t *result_pool,
2640 apr_pool_t *scratch_pool)
2642 svn_wc__db_status_t presence;
2644 SVN_ERR(svn_wc__db_base_get_info(&presence, NULL, NULL, NULL, NULL,
2645 NULL, NULL, NULL, NULL, NULL,
2646 NULL, NULL, NULL, NULL, props, NULL,
2648 result_pool, scratch_pool));
2649 if (presence != svn_wc__db_status_normal
2650 && presence != svn_wc__db_status_incomplete)
2652 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2653 _("The node '%s' has a BASE status that"
2654 " has no properties."),
2655 svn_dirent_local_style(local_abspath,
2659 return SVN_NO_ERROR;
2664 svn_wc__db_base_get_children(const apr_array_header_t **children,
2666 const char *local_abspath,
2667 apr_pool_t *result_pool,
2668 apr_pool_t *scratch_pool)
2670 svn_wc__db_wcroot_t *wcroot;
2671 const char *local_relpath;
2673 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2675 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2677 scratch_pool, scratch_pool));
2678 VERIFY_USABLE_WCROOT(wcroot);
2680 return gather_repo_children(children, wcroot, local_relpath, 0,
2681 result_pool, scratch_pool);
2686 svn_wc__db_base_set_dav_cache(svn_wc__db_t *db,
2687 const char *local_abspath,
2688 const apr_hash_t *props,
2689 apr_pool_t *scratch_pool)
2691 svn_sqlite__stmt_t *stmt;
2694 SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
2695 STMT_UPDATE_BASE_NODE_DAV_CACHE,
2697 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
2699 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2701 if (affected_rows != 1)
2702 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2703 _("The node '%s' was not found."),
2704 svn_dirent_local_style(local_abspath,
2707 return SVN_NO_ERROR;
2712 svn_wc__db_base_get_dav_cache(apr_hash_t **props,
2714 const char *local_abspath,
2715 apr_pool_t *result_pool,
2716 apr_pool_t *scratch_pool)
2718 svn_sqlite__stmt_t *stmt;
2719 svn_boolean_t have_row;
2721 SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
2722 STMT_SELECT_BASE_DAV_CACHE, scratch_pool));
2723 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2725 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
2726 svn_sqlite__reset(stmt),
2727 _("The node '%s' was not found."),
2728 svn_dirent_local_style(local_abspath,
2731 SVN_ERR(svn_sqlite__column_properties(props, stmt, 0, result_pool,
2733 return svn_error_trace(svn_sqlite__reset(stmt));
2738 svn_wc__db_base_clear_dav_cache_recursive(svn_wc__db_t *db,
2739 const char *local_abspath,
2740 apr_pool_t *scratch_pool)
2742 svn_wc__db_wcroot_t *wcroot;
2743 const char *local_relpath;
2744 svn_sqlite__stmt_t *stmt;
2746 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2748 scratch_pool, scratch_pool));
2749 VERIFY_USABLE_WCROOT(wcroot);
2751 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2752 STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE));
2753 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2755 SVN_ERR(svn_sqlite__step_done(stmt));
2757 return SVN_NO_ERROR;
2762 svn_wc__db_depth_get_info(svn_wc__db_status_t *status,
2763 svn_node_kind_t *kind,
2764 svn_revnum_t *revision,
2765 const char **repos_relpath,
2766 apr_int64_t *repos_id,
2767 svn_revnum_t *changed_rev,
2768 apr_time_t *changed_date,
2769 const char **changed_author,
2771 const svn_checksum_t **checksum,
2772 const char **target,
2773 svn_boolean_t *had_props,
2775 svn_wc__db_wcroot_t *wcroot,
2776 const char *local_relpath,
2778 apr_pool_t *result_pool,
2779 apr_pool_t *scratch_pool)
2781 svn_sqlite__stmt_t *stmt;
2782 svn_boolean_t have_row;
2783 svn_error_t *err = SVN_NO_ERROR;
2785 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2786 STMT_SELECT_DEPTH_NODE));
2787 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
2788 wcroot->wc_id, local_relpath, op_depth));
2789 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2793 svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2795 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2803 *status = node_status;
2806 SVN_ERR(convert_to_working_status(status, *status));
2808 repos_location_from_columns(repos_id, revision, repos_relpath,
2809 stmt, 0, 4, 1, result_pool);
2813 *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2817 *changed_date = svn_sqlite__column_int64(stmt, 8);
2821 /* Result may be NULL. */
2822 *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2826 if (node_kind != svn_node_dir)
2828 *depth = svn_depth_unknown;
2832 *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2838 if (node_kind != svn_node_file)
2844 err = svn_sqlite__column_checksum(checksum, stmt, 5,
2847 err = svn_error_createf(
2849 _("The node '%s' has a corrupt checksum value."),
2850 path_for_error_message(wcroot, local_relpath,
2856 if (node_kind != svn_node_symlink)
2859 *target = svn_sqlite__column_text(stmt, 11, result_pool);
2863 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2867 if (node_status == svn_wc__db_status_normal
2868 || node_status == svn_wc__db_status_incomplete)
2870 SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2871 result_pool, scratch_pool));
2873 *props = apr_hash_make(result_pool);
2877 assert(svn_sqlite__column_is_null(stmt, 13));
2884 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2885 _("The node '%s' was not found."),
2886 path_for_error_message(wcroot, local_relpath,
2890 /* Note: given the composition, no need to wrap for tracing. */
2891 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2895 /* Baton for passing args to with_triggers(). */
2896 struct with_triggers_baton_t {
2899 svn_wc__db_txn_callback_t cb_func;
2903 /* Helper for creating SQLite triggers, running the main transaction
2904 callback, and then dropping the triggers. It guarantees that the
2905 triggers will not survive the transaction. This could be used for
2906 any general prefix/postscript statements where the postscript
2907 *must* be executed if the transaction completes.
2909 Implements svn_wc__db_txn_callback_t. */
2910 static svn_error_t *
2911 with_triggers(void *baton,
2912 svn_wc__db_wcroot_t *wcroot,
2913 const char *local_relpath,
2914 apr_pool_t *scratch_pool)
2916 struct with_triggers_baton_t *b = baton;
2920 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, b->create_trigger));
2922 err1 = b->cb_func(b->cb_baton, wcroot, local_relpath, scratch_pool);
2924 err2 = svn_sqlite__exec_statements(wcroot->sdb, b->drop_trigger);
2926 return svn_error_trace(svn_error_compose_create(err1, err2));
2930 /* Prototype for the "work callback" used by with_finalization(). */
2931 typedef svn_error_t * (*work_callback_t)(
2933 svn_wc__db_wcroot_t *wcroot,
2934 svn_cancel_func_t cancel_func,
2936 svn_wc_notify_func2_t notify_func,
2938 apr_pool_t *scratch_pool);
2940 /* Utility function to provide several features, with a guaranteed
2941 finalization (ie. to drop temporary tables).
2943 1) for WCROOT and LOCAL_RELPATH, run TXN_CB(TXN_BATON) within a
2945 2) if (1) is successful and a NOTIFY_FUNC is provided, then run
2946 the "work" step: WORK_CB(WORK_BATON).
2947 3) execute FINALIZE_STMT_IDX no matter what errors may be thrown
2948 from the above two steps.
2950 CANCEL_FUNC, CANCEL_BATON, NOTIFY_FUNC and NOTIFY_BATON are their
2951 typical values. These are passed to the work callback, which typically
2952 provides notification about the work done by TXN_CB. */
2953 static svn_error_t *
2954 with_finalization(svn_wc__db_wcroot_t *wcroot,
2955 const char *local_relpath,
2956 svn_wc__db_txn_callback_t txn_cb,
2958 work_callback_t work_cb,
2960 svn_cancel_func_t cancel_func,
2962 svn_wc_notify_func2_t notify_func,
2964 int finalize_stmt_idx,
2965 apr_pool_t *scratch_pool)
2970 err1 = svn_wc__db_with_txn(wcroot, local_relpath, txn_cb, txn_baton,
2973 if (err1 == NULL && notify_func != NULL)
2975 err2 = work_cb(work_baton, wcroot,
2976 cancel_func, cancel_baton,
2977 notify_func, notify_baton,
2979 err1 = svn_error_compose_create(err1, err2);
2982 err2 = svn_sqlite__exec_statements(wcroot->sdb, finalize_stmt_idx);
2984 return svn_error_trace(svn_error_compose_create(err1, err2));
2988 /* Initialize the baton with appropriate "blank" values. This allows the
2989 insertion function to leave certain columns null. */
2991 blank_ieb(insert_external_baton_t *ieb)
2993 memset(ieb, 0, sizeof(*ieb));
2994 ieb->revision = SVN_INVALID_REVNUM;
2995 ieb->changed_rev = SVN_INVALID_REVNUM;
2996 ieb->repos_id = INVALID_REPOS_ID;
2998 ieb->recorded_peg_revision = SVN_INVALID_REVNUM;
2999 ieb->recorded_revision = SVN_INVALID_REVNUM;
3002 /* Insert the externals row represented by (insert_external_baton_t *) BATON.
3004 * Implements svn_wc__db_txn_callback_t. */
3005 static svn_error_t *
3006 insert_external_node(const insert_external_baton_t *ieb,
3007 svn_wc__db_wcroot_t *wcroot,
3008 const char *local_relpath,
3009 apr_pool_t *scratch_pool)
3011 svn_wc__db_status_t status;
3013 svn_boolean_t update_root;
3014 apr_int64_t repos_id;
3015 svn_sqlite__stmt_t *stmt;
3017 if (ieb->repos_id != INVALID_REPOS_ID)
3018 repos_id = ieb->repos_id;
3020 SVN_ERR(create_repos_id(&repos_id, ieb->repos_root_url, ieb->repos_uuid,
3021 wcroot->sdb, scratch_pool));
3023 /* And there must be no existing BASE node or it must be a file external */
3024 err = svn_wc__db_base_get_info_internal(&status, NULL, NULL, NULL, NULL,
3025 NULL, NULL, NULL, NULL, NULL,
3026 NULL, NULL, NULL, NULL, &update_root,
3027 wcroot, local_relpath,
3028 scratch_pool, scratch_pool);
3031 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3032 return svn_error_trace(err);
3034 svn_error_clear(err);
3036 else if (status == svn_wc__db_status_normal && !update_root)
3037 return svn_error_create(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, NULL);
3039 if (ieb->kind == svn_node_file
3040 || ieb->kind == svn_node_symlink)
3042 struct insert_base_baton_t ibb;
3046 ibb.status = svn_wc__db_status_normal;
3047 ibb.kind = ieb->kind;
3049 ibb.repos_id = repos_id;
3050 ibb.repos_relpath = ieb->repos_relpath;
3051 ibb.revision = ieb->revision;
3053 ibb.props = ieb->props;
3054 ibb.iprops = ieb->iprops;
3055 ibb.changed_rev = ieb->changed_rev;
3056 ibb.changed_date = ieb->changed_date;
3057 ibb.changed_author = ieb->changed_author;
3059 ibb.dav_cache = ieb->dav_cache;
3061 ibb.checksum = ieb->checksum;
3062 ibb.target = ieb->target;
3064 ibb.conflict = ieb->conflict;
3066 ibb.update_actual_props = ieb->update_actual_props;
3067 ibb.new_actual_props = ieb->new_actual_props;
3069 ibb.keep_recorded_info = ieb->keep_recorded_info;
3071 ibb.work_items = ieb->work_items;
3073 ibb.file_external = TRUE;
3075 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
3078 SVN_ERR(add_work_items(wcroot->sdb, ieb->work_items, scratch_pool));
3080 /* The externals table only support presence normal and excluded */
3081 SVN_ERR_ASSERT(ieb->presence == svn_wc__db_status_normal
3082 || ieb->presence == svn_wc__db_status_excluded);
3084 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_EXTERNAL));
3086 SVN_ERR(svn_sqlite__bindf(stmt, "issttsis",
3089 svn_relpath_dirname(local_relpath,
3091 presence_map, ieb->presence,
3092 kind_map, ieb->kind,
3093 ieb->record_ancestor_relpath,
3095 ieb->recorded_repos_relpath));
3097 if (SVN_IS_VALID_REVNUM(ieb->recorded_peg_revision))
3098 SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, ieb->recorded_peg_revision));
3100 if (SVN_IS_VALID_REVNUM(ieb->recorded_revision))
3101 SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, ieb->recorded_revision));
3103 SVN_ERR(svn_sqlite__insert(NULL, stmt));
3105 return SVN_NO_ERROR;
3109 svn_wc__db_external_add_file(svn_wc__db_t *db,
3110 const char *local_abspath,
3111 const char *wri_abspath,
3113 const char *repos_relpath,
3114 const char *repos_root_url,
3115 const char *repos_uuid,
3116 svn_revnum_t revision,
3118 const apr_hash_t *props,
3119 apr_array_header_t *iprops,
3121 svn_revnum_t changed_rev,
3122 apr_time_t changed_date,
3123 const char *changed_author,
3125 const svn_checksum_t *checksum,
3127 const apr_hash_t *dav_cache,
3129 const char *record_ancestor_abspath,
3130 const char *recorded_repos_relpath,
3131 svn_revnum_t recorded_peg_revision,
3132 svn_revnum_t recorded_revision,
3134 svn_boolean_t update_actual_props,
3135 apr_hash_t *new_actual_props,
3137 svn_boolean_t keep_recorded_info,
3138 const svn_skel_t *conflict,
3139 const svn_skel_t *work_items,
3140 apr_pool_t *scratch_pool)
3142 svn_wc__db_wcroot_t *wcroot;
3143 const char *local_relpath;
3144 insert_external_baton_t ieb;
3146 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3149 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3151 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3152 wri_abspath, scratch_pool, scratch_pool));
3153 VERIFY_USABLE_WCROOT(wcroot);
3155 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3156 record_ancestor_abspath));
3158 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3160 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3164 ieb.kind = svn_node_file;
3165 ieb.presence = svn_wc__db_status_normal;
3167 ieb.repos_root_url = repos_root_url;
3168 ieb.repos_uuid = repos_uuid;
3170 ieb.repos_relpath = repos_relpath;
3171 ieb.revision = revision;
3174 ieb.iprops = iprops;
3176 ieb.changed_rev = changed_rev;
3177 ieb.changed_date = changed_date;
3178 ieb.changed_author = changed_author;
3180 ieb.checksum = checksum;
3182 ieb.dav_cache = dav_cache;
3184 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3186 record_ancestor_abspath);
3187 ieb.recorded_repos_relpath = recorded_repos_relpath;
3188 ieb.recorded_peg_revision = recorded_peg_revision;
3189 ieb.recorded_revision = recorded_revision;
3191 ieb.update_actual_props = update_actual_props;
3192 ieb.new_actual_props = new_actual_props;
3194 ieb.keep_recorded_info = keep_recorded_info;
3196 ieb.conflict = conflict;
3197 ieb.work_items = work_items;
3199 SVN_WC__DB_WITH_TXN(
3200 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3203 return SVN_NO_ERROR;
3207 svn_wc__db_external_add_symlink(svn_wc__db_t *db,
3208 const char *local_abspath,
3209 const char *wri_abspath,
3210 const char *repos_relpath,
3211 const char *repos_root_url,
3212 const char *repos_uuid,
3213 svn_revnum_t revision,
3214 const apr_hash_t *props,
3215 svn_revnum_t changed_rev,
3216 apr_time_t changed_date,
3217 const char *changed_author,
3219 const apr_hash_t *dav_cache,
3220 const char *record_ancestor_abspath,
3221 const char *recorded_repos_relpath,
3222 svn_revnum_t recorded_peg_revision,
3223 svn_revnum_t recorded_revision,
3224 svn_boolean_t update_actual_props,
3225 apr_hash_t *new_actual_props,
3226 svn_boolean_t keep_recorded_info,
3227 const svn_skel_t *work_items,
3228 apr_pool_t *scratch_pool)
3230 svn_wc__db_wcroot_t *wcroot;
3231 const char *local_relpath;
3232 insert_external_baton_t ieb;
3234 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3237 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3239 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3240 wri_abspath, scratch_pool, scratch_pool));
3241 VERIFY_USABLE_WCROOT(wcroot);
3243 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3244 record_ancestor_abspath));
3246 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3248 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3252 ieb.kind = svn_node_symlink;
3253 ieb.presence = svn_wc__db_status_normal;
3255 ieb.repos_root_url = repos_root_url;
3256 ieb.repos_uuid = repos_uuid;
3258 ieb.repos_relpath = repos_relpath;
3259 ieb.revision = revision;
3263 ieb.changed_rev = changed_rev;
3264 ieb.changed_date = changed_date;
3265 ieb.changed_author = changed_author;
3267 ieb.target = target;
3269 ieb.dav_cache = dav_cache;
3271 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3273 record_ancestor_abspath);
3274 ieb.recorded_repos_relpath = recorded_repos_relpath;
3275 ieb.recorded_peg_revision = recorded_peg_revision;
3276 ieb.recorded_revision = recorded_revision;
3278 ieb.update_actual_props = update_actual_props;
3279 ieb.new_actual_props = new_actual_props;
3281 ieb.keep_recorded_info = keep_recorded_info;
3283 ieb.work_items = work_items;
3285 SVN_WC__DB_WITH_TXN(
3286 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3289 return SVN_NO_ERROR;
3293 svn_wc__db_external_add_dir(svn_wc__db_t *db,
3294 const char *local_abspath,
3295 const char *wri_abspath,
3296 const char *repos_root_url,
3297 const char *repos_uuid,
3298 const char *record_ancestor_abspath,
3299 const char *recorded_repos_relpath,
3300 svn_revnum_t recorded_peg_revision,
3301 svn_revnum_t recorded_revision,
3302 const svn_skel_t *work_items,
3303 apr_pool_t *scratch_pool)
3305 svn_wc__db_wcroot_t *wcroot;
3306 const char *local_relpath;
3307 insert_external_baton_t ieb;
3309 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3312 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3314 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3315 wri_abspath, scratch_pool, scratch_pool));
3316 VERIFY_USABLE_WCROOT(wcroot);
3318 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3319 record_ancestor_abspath));
3321 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3323 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3327 ieb.kind = svn_node_dir;
3328 ieb.presence = svn_wc__db_status_normal;
3330 ieb.repos_root_url = repos_root_url;
3331 ieb.repos_uuid = repos_uuid;
3333 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3335 record_ancestor_abspath);
3336 ieb.recorded_repos_relpath = recorded_repos_relpath;
3337 ieb.recorded_peg_revision = recorded_peg_revision;
3338 ieb.recorded_revision = recorded_revision;
3340 ieb.work_items = work_items;
3342 SVN_WC__DB_WITH_TXN(
3343 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3346 return SVN_NO_ERROR;
3349 /* The body of svn_wc__db_external_remove(). */
3350 static svn_error_t *
3351 db_external_remove(const svn_skel_t *work_items,
3352 svn_wc__db_wcroot_t *wcroot,
3353 const char *local_relpath,
3354 apr_pool_t *scratch_pool)
3356 svn_sqlite__stmt_t *stmt;
3358 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3359 STMT_DELETE_EXTERNAL));
3360 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3361 SVN_ERR(svn_sqlite__step_done(stmt));
3363 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
3365 /* ### What about actual? */
3366 return SVN_NO_ERROR;
3370 svn_wc__db_external_remove(svn_wc__db_t *db,
3371 const char *local_abspath,
3372 const char *wri_abspath,
3373 const svn_skel_t *work_items,
3374 apr_pool_t *scratch_pool)
3376 svn_wc__db_wcroot_t *wcroot;
3377 const char *local_relpath;
3379 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3382 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3384 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3385 wri_abspath, scratch_pool, scratch_pool));
3386 VERIFY_USABLE_WCROOT(wcroot);
3388 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3390 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3392 SVN_WC__DB_WITH_TXN(db_external_remove(work_items, wcroot, local_relpath,
3396 return SVN_NO_ERROR;
3400 svn_wc__db_external_read(svn_wc__db_status_t *status,
3401 svn_node_kind_t *kind,
3402 const char **definining_abspath,
3403 const char **repos_root_url,
3404 const char **repos_uuid,
3405 const char **recorded_repos_relpath,
3406 svn_revnum_t *recorded_peg_revision,
3407 svn_revnum_t *recorded_revision,
3409 const char *local_abspath,
3410 const char *wri_abspath,
3411 apr_pool_t *result_pool,
3412 apr_pool_t *scratch_pool)
3414 svn_wc__db_wcroot_t *wcroot;
3415 const char *local_relpath;
3416 svn_sqlite__stmt_t *stmt;
3417 svn_boolean_t have_info;
3418 svn_error_t *err = NULL;
3419 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3422 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3424 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3425 wri_abspath, scratch_pool, scratch_pool));
3426 VERIFY_USABLE_WCROOT(wcroot);
3428 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3430 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3432 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3433 STMT_SELECT_EXTERNAL_INFO));
3434 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3435 SVN_ERR(svn_sqlite__step(&have_info, stmt));
3440 *status = svn_sqlite__column_token(stmt, 0, presence_map);
3443 *kind = svn_sqlite__column_token(stmt, 1, kind_map);
3445 if (definining_abspath)
3447 const char *record_relpath = svn_sqlite__column_text(stmt, 2, NULL);
3449 *definining_abspath = svn_dirent_join(wcroot->abspath,
3450 record_relpath, result_pool);
3453 if (repos_root_url || repos_uuid)
3455 apr_int64_t repos_id;
3457 repos_id = svn_sqlite__column_int64(stmt, 3);
3459 err = svn_error_compose_create(
3461 svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
3462 wcroot->sdb, repos_id,
3466 if (recorded_repos_relpath)
3467 *recorded_repos_relpath = svn_sqlite__column_text(stmt, 4,
3470 if (recorded_peg_revision)
3471 *recorded_peg_revision = svn_sqlite__column_revnum(stmt, 5);
3473 if (recorded_revision)
3474 *recorded_revision = svn_sqlite__column_revnum(stmt, 6);
3478 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3479 _("The node '%s' is not an external."),
3480 svn_dirent_local_style(local_abspath,
3484 return svn_error_trace(
3485 svn_error_compose_create(err, svn_sqlite__reset(stmt)));
3489 svn_wc__db_committable_externals_below(apr_array_header_t **externals,
3491 const char *local_abspath,
3492 svn_boolean_t immediates_only,
3493 apr_pool_t *result_pool,
3494 apr_pool_t *scratch_pool)
3496 svn_wc__db_wcroot_t *wcroot;
3497 svn_sqlite__stmt_t *stmt;
3498 const char *local_relpath;
3499 svn_boolean_t have_row;
3500 svn_wc__committable_external_info_t *info;
3501 svn_node_kind_t db_kind;
3502 apr_array_header_t *result = NULL;
3504 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3506 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3507 local_abspath, scratch_pool, scratch_pool));
3508 VERIFY_USABLE_WCROOT(wcroot);
3510 SVN_ERR(svn_sqlite__get_statement(
3513 ? STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW
3514 : STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW));
3516 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3518 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3521 result = apr_array_make(result_pool, 0,
3522 sizeof(svn_wc__committable_external_info_t *));
3526 info = apr_palloc(result_pool, sizeof(*info));
3528 local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3529 info->local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
3532 db_kind = svn_sqlite__column_token(stmt, 1, kind_map);
3533 SVN_ERR_ASSERT(db_kind == svn_node_file || db_kind == svn_node_dir);
3534 info->kind = db_kind;
3536 info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
3537 info->repos_root_url = svn_sqlite__column_text(stmt, 3, result_pool);
3539 APR_ARRAY_PUSH(result, svn_wc__committable_external_info_t *) = info;
3541 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3544 *externals = result;
3545 return svn_error_trace(svn_sqlite__reset(stmt));
3549 svn_wc__db_externals_defined_below(apr_hash_t **externals,
3551 const char *local_abspath,
3552 apr_pool_t *result_pool,
3553 apr_pool_t *scratch_pool)
3555 svn_wc__db_wcroot_t *wcroot;
3556 svn_sqlite__stmt_t *stmt;
3557 const char *local_relpath;
3558 svn_boolean_t have_row;
3560 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3562 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3563 local_abspath, scratch_pool, scratch_pool));
3564 VERIFY_USABLE_WCROOT(wcroot);
3566 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3567 STMT_SELECT_EXTERNALS_DEFINED));
3569 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3571 *externals = apr_hash_make(result_pool);
3572 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3576 const char *def_local_relpath;
3578 local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3579 def_local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3581 svn_hash_sets(*externals,
3582 svn_dirent_join(wcroot->abspath, local_relpath,
3584 svn_dirent_join(wcroot->abspath, def_local_relpath,
3587 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3590 return svn_error_trace(svn_sqlite__reset(stmt));
3594 svn_wc__db_externals_gather_definitions(apr_hash_t **externals,
3595 apr_hash_t **depths,
3597 const char *local_abspath,
3598 apr_pool_t *result_pool,
3599 apr_pool_t *scratch_pool)
3601 svn_wc__db_wcroot_t *wcroot;
3602 svn_sqlite__stmt_t *stmt;
3603 const char *local_relpath;
3604 svn_boolean_t have_row;
3605 svn_error_t *err = NULL;
3606 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3608 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3610 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3611 local_abspath, scratch_pool, iterpool));
3612 VERIFY_USABLE_WCROOT(wcroot);
3614 *externals = apr_hash_make(result_pool);
3616 *depths = apr_hash_make(result_pool);
3618 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3619 STMT_SELECT_EXTERNAL_PROPERTIES));
3621 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3623 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3627 apr_hash_t *node_props;
3628 const char *external_value;
3630 svn_pool_clear(iterpool);
3631 err = svn_sqlite__column_properties(&node_props, stmt, 0, iterpool,
3637 external_value = svn_prop_get_value(node_props, SVN_PROP_EXTERNALS);
3641 const char *node_abspath;
3642 const char *node_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3644 node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
3647 svn_hash_sets(*externals, node_abspath,
3648 apr_pstrdup(result_pool, external_value));
3653 = svn_sqlite__column_token_null(stmt, 2, depth_map,
3656 svn_hash_sets(*depths, node_abspath,
3657 /* Use static string */
3658 svn_token__to_word(depth_map, depth));
3662 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3665 svn_pool_destroy(iterpool);
3667 return svn_error_trace(svn_error_compose_create(err,
3668 svn_sqlite__reset(stmt)));
3671 /* Copy the ACTUAL data for SRC_RELPATH and tweak it to refer to DST_RELPATH.
3672 The new ACTUAL data won't have any conflicts. */
3673 static svn_error_t *
3674 copy_actual(svn_wc__db_wcroot_t *src_wcroot,
3675 const char *src_relpath,
3676 svn_wc__db_wcroot_t *dst_wcroot,
3677 const char *dst_relpath,
3678 apr_pool_t *scratch_pool)
3680 svn_sqlite__stmt_t *stmt;
3681 svn_boolean_t have_row;
3683 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
3684 STMT_SELECT_ACTUAL_NODE));
3685 SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
3686 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3689 apr_size_t props_size;
3690 const char *changelist;
3691 const char *properties;
3693 /* Skipping conflict data... */
3694 changelist = svn_sqlite__column_text(stmt, 0, scratch_pool);
3695 /* No need to parse the properties when simply copying. */
3696 properties = svn_sqlite__column_blob(stmt, 1, &props_size, scratch_pool);
3698 if (changelist || properties)
3700 SVN_ERR(svn_sqlite__reset(stmt));
3702 SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
3703 STMT_INSERT_ACTUAL_NODE));
3704 SVN_ERR(svn_sqlite__bindf(stmt, "issbs",
3705 dst_wcroot->wc_id, dst_relpath,
3706 svn_relpath_dirname(dst_relpath, scratch_pool),
3707 properties, props_size, changelist));
3708 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3711 SVN_ERR(svn_sqlite__reset(stmt));
3713 return SVN_NO_ERROR;
3716 /* Helper for svn_wc__db_op_copy to handle copying from one db to
3718 static svn_error_t *
3719 cross_db_copy(svn_wc__db_wcroot_t *src_wcroot,
3720 const char *src_relpath,
3721 svn_wc__db_wcroot_t *dst_wcroot,
3722 const char *dst_relpath,
3723 svn_wc__db_status_t dst_status,
3725 int dst_np_op_depth,
3726 svn_node_kind_t kind,
3727 const apr_array_header_t *children,
3728 apr_int64_t copyfrom_id,
3729 const char *copyfrom_relpath,
3730 svn_revnum_t copyfrom_rev,
3731 apr_pool_t *scratch_pool)
3733 insert_working_baton_t iwb;
3734 svn_revnum_t changed_rev;
3735 apr_time_t changed_date;
3736 const char *changed_author;
3737 const svn_checksum_t *checksum;
3741 SVN_ERR_ASSERT(kind == svn_node_file
3742 || kind == svn_node_dir
3745 SVN_ERR(read_info(NULL, NULL, NULL, NULL, NULL,
3746 &changed_rev, &changed_date, &changed_author, &depth,
3747 &checksum, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3748 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3749 src_wcroot, src_relpath, scratch_pool, scratch_pool));
3751 SVN_ERR(db_read_pristine_props(&props, src_wcroot, src_relpath, FALSE,
3752 scratch_pool, scratch_pool));
3755 iwb.presence = dst_status;
3759 iwb.changed_rev = changed_rev;
3760 iwb.changed_date = changed_date;
3761 iwb.changed_author = changed_author;
3762 iwb.original_repos_id = copyfrom_id;
3763 iwb.original_repos_relpath = copyfrom_relpath;
3764 iwb.original_revnum = copyfrom_rev;
3765 iwb.moved_here = FALSE;
3767 iwb.op_depth = dst_op_depth;
3769 iwb.checksum = checksum;
3770 iwb.children = children;
3773 iwb.not_present_op_depth = dst_np_op_depth;
3775 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath, scratch_pool));
3777 SVN_ERR(copy_actual(src_wcroot, src_relpath,
3778 dst_wcroot, dst_relpath, scratch_pool));
3780 return SVN_NO_ERROR;
3783 /* Helper for scan_deletion_txn. Extracts the moved-to information, if
3784 any, from STMT. Sets *SCAN to FALSE if moved-to was available. */
3785 static svn_error_t *
3786 get_moved_to(const char **moved_to_relpath_p,
3787 const char **moved_to_op_root_relpath_p,
3788 svn_boolean_t *scan,
3789 svn_sqlite__stmt_t *stmt,
3790 const char *current_relpath,
3791 svn_wc__db_wcroot_t *wcroot,
3792 const char *local_relpath,
3793 apr_pool_t *result_pool,
3794 apr_pool_t *scratch_pool)
3796 const char *moved_to_relpath = svn_sqlite__column_text(stmt, 3, NULL);
3798 if (moved_to_relpath)
3800 const char *moved_to_op_root_relpath = moved_to_relpath;
3802 if (strcmp(current_relpath, local_relpath))
3804 /* LOCAL_RELPATH is a child inside the move op-root. */
3805 const char *moved_child_relpath;
3807 /* The CURRENT_RELPATH is the op_root of the delete-half of
3808 * the move. LOCAL_RELPATH is a child that was moved along.
3809 * Compute the child's new location within the move target. */
3810 moved_child_relpath = svn_relpath_skip_ancestor(current_relpath,
3812 SVN_ERR_ASSERT(moved_child_relpath &&
3813 strlen(moved_child_relpath) > 0);
3814 moved_to_relpath = svn_relpath_join(moved_to_op_root_relpath,
3815 moved_child_relpath,
3819 if (moved_to_op_root_relpath && moved_to_op_root_relpath_p)
3820 *moved_to_op_root_relpath_p
3821 = apr_pstrdup(result_pool, moved_to_op_root_relpath);
3823 if (moved_to_relpath && moved_to_relpath_p)
3825 = apr_pstrdup(result_pool, moved_to_relpath);
3830 return SVN_NO_ERROR;
3834 /* The body of svn_wc__db_scan_deletion().
3836 static svn_error_t *
3837 scan_deletion_txn(const char **base_del_relpath,
3838 const char **moved_to_relpath,
3839 const char **work_del_relpath,
3840 const char **moved_to_op_root_relpath,
3841 svn_wc__db_wcroot_t *wcroot,
3842 const char *local_relpath,
3843 apr_pool_t *result_pool,
3844 apr_pool_t *scratch_pool)
3846 const char *current_relpath = local_relpath;
3847 svn_sqlite__stmt_t *stmt;
3848 svn_wc__db_status_t work_presence;
3849 svn_boolean_t have_row, scan, have_base;
3852 /* Initialize all the OUT parameters. */
3853 if (base_del_relpath != NULL)
3854 *base_del_relpath = NULL;
3855 if (moved_to_relpath != NULL)
3856 *moved_to_relpath = NULL;
3857 if (work_del_relpath != NULL)
3858 *work_del_relpath = NULL;
3859 if (moved_to_op_root_relpath != NULL)
3860 *moved_to_op_root_relpath = NULL;
3862 /* If looking for moved-to info then we need to scan every path
3863 until we find it. If not looking for moved-to we only need to
3864 check op-roots and parents of op-roots. */
3865 scan = (moved_to_op_root_relpath || moved_to_relpath);
3867 SVN_ERR(svn_sqlite__get_statement(
3869 scan ? STMT_SELECT_DELETION_INFO_SCAN
3870 : STMT_SELECT_DELETION_INFO));
3872 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
3873 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3875 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
3876 _("The node '%s' was not found."),
3877 path_for_error_message(wcroot, local_relpath,
3880 work_presence = svn_sqlite__column_token(stmt, 1, presence_map);
3881 have_base = !svn_sqlite__column_is_null(stmt, 0);
3882 if (work_presence != svn_wc__db_status_not_present
3883 && work_presence != svn_wc__db_status_base_deleted)
3884 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
3885 svn_sqlite__reset(stmt),
3886 _("Expected node '%s' to be deleted."),
3887 path_for_error_message(wcroot, local_relpath,
3890 op_depth = svn_sqlite__column_int(stmt, 2);
3892 /* Special case: LOCAL_RELPATH not-present within a WORKING tree, we
3893 treat this as an op-root. At commit time we need to explicitly
3894 delete such nodes otherwise they will be present in the
3896 if (work_presence == svn_wc__db_status_not_present
3897 && work_del_relpath && !*work_del_relpath)
3899 *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
3901 if (!scan && !base_del_relpath)
3903 /* We have all we need, exit early */
3904 SVN_ERR(svn_sqlite__reset(stmt));
3905 return SVN_NO_ERROR;
3913 const char *parent_relpath;
3914 int current_depth = relpath_depth(current_relpath);
3916 /* Step CURRENT_RELPATH to op-root */
3922 err = get_moved_to(moved_to_relpath, moved_to_op_root_relpath,
3923 &scan, stmt, current_relpath,
3924 wcroot, local_relpath,
3925 result_pool, scratch_pool);
3927 && !base_del_relpath
3928 && !work_del_relpath))
3930 /* We have all we need (or an error occurred) */
3931 SVN_ERR(svn_sqlite__reset(stmt));
3932 return svn_error_trace(err);
3936 if (current_depth <= op_depth)
3939 current_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
3942 if (scan || current_depth == op_depth)
3944 SVN_ERR(svn_sqlite__reset(stmt));
3945 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
3947 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3948 SVN_ERR_ASSERT(have_row);
3949 have_base = !svn_sqlite__column_is_null(stmt, 0);
3952 SVN_ERR(svn_sqlite__reset(stmt));
3954 /* Now CURRENT_RELPATH is an op-root, have a look at the parent. */
3956 SVN_ERR_ASSERT(current_relpath[0] != '\0'); /* Catch invalid data */
3957 parent_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
3958 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
3959 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3962 /* No row means no WORKING node which mean we just fell off
3963 the WORKING tree, so CURRENT_RELPATH is the op-root
3964 closest to the wc root. */
3965 if (have_base && base_del_relpath)
3966 *base_del_relpath = apr_pstrdup(result_pool, current_relpath);
3970 /* Still in the WORKING tree so the first time we get here
3971 CURRENT_RELPATH is a delete op-root in the WORKING tree. */
3972 if (work_del_relpath && !*work_del_relpath)
3974 *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
3976 if (!scan && !base_del_relpath)
3977 break; /* We have all we need */
3980 current_relpath = parent_relpath;
3981 op_depth = svn_sqlite__column_int(stmt, 2);
3982 have_base = !svn_sqlite__column_is_null(stmt, 0);
3985 SVN_ERR(svn_sqlite__reset(stmt));
3987 return SVN_NO_ERROR;
3991 svn_wc__db_scan_deletion(const char **base_del_abspath,
3992 const char **moved_to_abspath,
3993 const char **work_del_abspath,
3994 const char **moved_to_op_root_abspath,
3996 const char *local_abspath,
3997 apr_pool_t *result_pool,
3998 apr_pool_t *scratch_pool)
4000 svn_wc__db_wcroot_t *wcroot;
4001 const char *local_relpath;
4002 const char *base_del_relpath, *moved_to_relpath, *work_del_relpath;
4003 const char *moved_to_op_root_relpath;
4005 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4007 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4008 local_abspath, scratch_pool, scratch_pool));
4009 VERIFY_USABLE_WCROOT(wcroot);
4011 SVN_WC__DB_WITH_TXN(
4012 scan_deletion_txn(&base_del_relpath, &moved_to_relpath,
4013 &work_del_relpath, &moved_to_op_root_relpath,
4014 wcroot, local_relpath, result_pool, scratch_pool),
4017 if (base_del_abspath)
4019 *base_del_abspath = (base_del_relpath
4020 ? svn_dirent_join(wcroot->abspath,
4021 base_del_relpath, result_pool)
4024 if (moved_to_abspath)
4026 *moved_to_abspath = (moved_to_relpath
4027 ? svn_dirent_join(wcroot->abspath,
4028 moved_to_relpath, result_pool)
4031 if (work_del_abspath)
4033 *work_del_abspath = (work_del_relpath
4034 ? svn_dirent_join(wcroot->abspath,
4035 work_del_relpath, result_pool)
4038 if (moved_to_op_root_abspath)
4040 *moved_to_op_root_abspath = (moved_to_op_root_relpath
4041 ? svn_dirent_join(wcroot->abspath,
4042 moved_to_op_root_relpath,
4047 return SVN_NO_ERROR;
4051 /* Set *COPYFROM_ID, *COPYFROM_RELPATH, *COPYFROM_REV to the values
4052 appropriate for the copy. Also return *STATUS, *KIND and *HAVE_WORK, *OP_ROOT
4053 since they are available. This is a helper for
4054 svn_wc__db_op_copy. */
4055 static svn_error_t *
4056 get_info_for_copy(apr_int64_t *copyfrom_id,
4057 const char **copyfrom_relpath,
4058 svn_revnum_t *copyfrom_rev,
4059 svn_wc__db_status_t *status,
4060 svn_node_kind_t *kind,
4061 svn_boolean_t *op_root,
4062 svn_wc__db_wcroot_t *wcroot,
4063 const char *local_relpath,
4064 apr_pool_t *result_pool,
4065 apr_pool_t *scratch_pool)
4067 const char *repos_relpath;
4068 svn_revnum_t revision;
4069 svn_wc__db_status_t node_status;
4070 apr_int64_t repos_id;
4071 svn_boolean_t is_op_root;
4073 SVN_ERR(read_info(&node_status, kind, &revision, &repos_relpath, &repos_id,
4074 NULL, NULL, NULL, NULL, NULL, NULL, copyfrom_relpath,
4075 copyfrom_id, copyfrom_rev, NULL, NULL, NULL, NULL,
4076 NULL, &is_op_root, NULL, NULL,
4077 NULL /* have_base */,
4078 NULL /* have_more_work */,
4079 NULL /* have_work */,
4080 wcroot, local_relpath, result_pool, scratch_pool));
4083 *op_root = is_op_root;
4085 if (node_status == svn_wc__db_status_excluded)
4087 /* The parent cannot be excluded, so look at the parent and then
4088 adjust the relpath */
4089 const char *parent_relpath, *base_name;
4091 svn_dirent_split(&parent_relpath, &base_name, local_relpath,
4093 SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev,
4095 wcroot, parent_relpath,
4096 scratch_pool, scratch_pool));
4097 if (*copyfrom_relpath)
4098 *copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, base_name,
4101 else if (node_status == svn_wc__db_status_added)
4103 SVN_ERR(scan_addition(&node_status, NULL, NULL, NULL, NULL, NULL, NULL,
4104 NULL, NULL, NULL, wcroot, local_relpath,
4105 scratch_pool, scratch_pool));
4107 else if (node_status == svn_wc__db_status_deleted && is_op_root)
4109 const char *base_del_relpath, *work_del_relpath;
4111 SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
4113 NULL, wcroot, local_relpath,
4114 scratch_pool, scratch_pool));
4115 if (work_del_relpath)
4117 const char *op_root_relpath;
4118 const char *parent_del_relpath = svn_relpath_dirname(work_del_relpath,
4121 /* Similar to, but not the same as, the _scan_addition and
4122 _join above. Can we use get_copyfrom here? */
4123 SVN_ERR(scan_addition(NULL, &op_root_relpath,
4124 NULL, NULL, /* repos_* */
4125 copyfrom_relpath, copyfrom_id, copyfrom_rev,
4126 NULL, NULL, NULL, wcroot, parent_del_relpath,
4127 scratch_pool, scratch_pool));
4129 = svn_relpath_join(*copyfrom_relpath,
4130 svn_relpath_skip_ancestor(op_root_relpath,
4134 else if (base_del_relpath)
4136 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, copyfrom_rev,
4138 copyfrom_id, NULL, NULL,
4139 NULL, NULL, NULL, NULL,
4140 NULL, NULL, NULL, NULL,
4141 wcroot, local_relpath,
4146 SVN_ERR_MALFUNCTION();
4148 else if (node_status == svn_wc__db_status_deleted)
4150 /* Keep original_* from read_info() to allow seeing the difference
4151 between base-deleted and not present */
4155 *copyfrom_relpath = repos_relpath;
4156 *copyfrom_rev = revision;
4157 *copyfrom_id = repos_id;
4161 *status = node_status;
4163 return SVN_NO_ERROR;
4167 /* Set *OP_DEPTH to the highest op depth of WCROOT:LOCAL_RELPATH. */
4168 static svn_error_t *
4169 op_depth_of(int *op_depth,
4170 svn_wc__db_wcroot_t *wcroot,
4171 const char *local_relpath)
4173 svn_sqlite__stmt_t *stmt;
4174 svn_boolean_t have_row;
4176 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4177 STMT_SELECT_NODE_INFO));
4178 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4179 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4180 SVN_ERR_ASSERT(have_row);
4181 *op_depth = svn_sqlite__column_int(stmt, 0);
4182 SVN_ERR(svn_sqlite__reset(stmt));
4184 return SVN_NO_ERROR;
4188 /* Determine at which OP_DEPTH a copy of COPYFROM_REPOS_ID, COPYFROM_RELPATH at
4189 revision COPYFROM_REVISION should be inserted as LOCAL_RELPATH. Do this
4190 by checking if this would be a direct child of a copy of its parent
4191 directory. If it is then set *OP_DEPTH to the op_depth of its parent.
4193 If the node is not a direct copy at the same revision of the parent
4194 *NP_OP_DEPTH will be set to the op_depth of the parent when a not-present
4195 node should be inserted at this op_depth. This will be the case when the
4196 parent already defined an incomplete child with the same name. Otherwise
4197 *NP_OP_DEPTH will be set to -1.
4199 If the parent node is not the parent of the to be copied node, then
4200 *OP_DEPTH will be set to the proper op_depth for a new operation root.
4202 Set *PARENT_OP_DEPTH to the op_depth of the parent.
4205 static svn_error_t *
4206 op_depth_for_copy(int *op_depth,
4208 int *parent_op_depth,
4209 apr_int64_t copyfrom_repos_id,
4210 const char *copyfrom_relpath,
4211 svn_revnum_t copyfrom_revision,
4212 svn_wc__db_wcroot_t *wcroot,
4213 const char *local_relpath,
4214 apr_pool_t *scratch_pool)
4216 const char *parent_relpath, *name;
4217 svn_sqlite__stmt_t *stmt;
4218 svn_boolean_t have_row;
4219 int incomplete_op_depth = -1;
4220 int min_op_depth = 1; /* Never touch BASE */
4222 *op_depth = relpath_depth(local_relpath);
4225 svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool);
4226 *parent_op_depth = relpath_depth(parent_relpath);
4228 if (!copyfrom_relpath)
4229 return SVN_NO_ERROR;
4231 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4232 STMT_SELECT_WORKING_NODE));
4233 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4234 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4237 svn_wc__db_status_t status = svn_sqlite__column_token(stmt, 1,
4240 min_op_depth = svn_sqlite__column_int(stmt, 0);
4241 if (status == svn_wc__db_status_incomplete)
4242 incomplete_op_depth = min_op_depth;
4244 SVN_ERR(svn_sqlite__reset(stmt));
4246 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4247 STMT_SELECT_WORKING_NODE));
4248 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4249 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4252 svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 1,
4255 *parent_op_depth = svn_sqlite__column_int(stmt, 0);
4256 if (*parent_op_depth < min_op_depth)
4258 /* We want to create a copy; not overwrite the lower layers */
4259 SVN_ERR(svn_sqlite__reset(stmt));
4260 return SVN_NO_ERROR;
4263 /* You can only add children below a node that exists.
4264 In WORKING that must be status added, which is represented
4265 as presence normal */
4266 SVN_ERR_ASSERT(presence == svn_wc__db_status_normal);
4268 if ((incomplete_op_depth < 0)
4269 || (incomplete_op_depth == *parent_op_depth))
4271 apr_int64_t parent_copyfrom_repos_id
4272 = svn_sqlite__column_int64(stmt, 10);
4273 const char *parent_copyfrom_relpath
4274 = svn_sqlite__column_text(stmt, 11, NULL);
4275 svn_revnum_t parent_copyfrom_revision
4276 = svn_sqlite__column_revnum(stmt, 12);
4278 if (parent_copyfrom_repos_id == copyfrom_repos_id)
4280 if (copyfrom_revision == parent_copyfrom_revision
4281 && !strcmp(copyfrom_relpath,
4282 svn_relpath_join(parent_copyfrom_relpath, name,
4284 *op_depth = *parent_op_depth;
4285 else if (incomplete_op_depth > 0)
4286 *np_op_depth = incomplete_op_depth;
4290 SVN_ERR(svn_sqlite__reset(stmt));
4292 return SVN_NO_ERROR;
4296 /* Like svn_wc__db_op_copy(), but with WCROOT+LOCAL_RELPATH
4297 * instead of DB+LOCAL_ABSPATH. A non-zero MOVE_OP_DEPTH implies that the
4298 * copy operation is part of a move, and indicates the op-depth of the
4299 * move destination op-root. */
4300 static svn_error_t *
4301 db_op_copy(svn_wc__db_wcroot_t *src_wcroot,
4302 const char *src_relpath,
4303 svn_wc__db_wcroot_t *dst_wcroot,
4304 const char *dst_relpath,
4305 const svn_skel_t *work_items,
4307 apr_pool_t *scratch_pool)
4309 const char *copyfrom_relpath;
4310 svn_revnum_t copyfrom_rev;
4311 svn_wc__db_status_t status;
4312 svn_wc__db_status_t dst_presence;
4313 svn_boolean_t op_root;
4314 apr_int64_t copyfrom_id;
4316 int dst_np_op_depth;
4317 int dst_parent_op_depth;
4318 svn_node_kind_t kind;
4319 const apr_array_header_t *children;
4321 SVN_ERR(get_info_for_copy(©from_id, ©from_relpath, ©from_rev,
4322 &status, &kind, &op_root, src_wcroot,
4323 src_relpath, scratch_pool, scratch_pool));
4325 SVN_ERR(op_depth_for_copy(&dst_op_depth, &dst_np_op_depth,
4326 &dst_parent_op_depth,
4327 copyfrom_id, copyfrom_relpath, copyfrom_rev,
4328 dst_wcroot, dst_relpath, scratch_pool));
4330 SVN_ERR_ASSERT(kind == svn_node_file || kind == svn_node_dir);
4332 /* ### New status, not finished, see notes/wc-ng/copying */
4335 case svn_wc__db_status_normal:
4336 case svn_wc__db_status_added:
4337 case svn_wc__db_status_moved_here:
4338 case svn_wc__db_status_copied:
4339 dst_presence = svn_wc__db_status_normal;
4341 case svn_wc__db_status_deleted:
4344 /* If the lower layer is already shadowcopied we can skip adding
4345 a not present node. */
4347 svn_wc__db_status_t dst_status;
4349 err = read_info(&dst_status, NULL, NULL, NULL, NULL, NULL, NULL,
4350 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4351 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4352 dst_wcroot, dst_relpath, scratch_pool, scratch_pool);
4356 if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
4357 svn_error_clear(err);
4359 return svn_error_trace(err);
4361 else if (dst_status == svn_wc__db_status_deleted)
4363 /* Node is already deleted; skip the NODES work, but do
4364 install wq items if requested */
4365 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4367 return SVN_NO_ERROR;
4372 /* This node is either a not-present node (which should be copied), or
4373 a base-delete of some lower layer (which shouldn't).
4374 Subversion <= 1.7 always added a not-present node here, which is
4375 safe (as it postpones the hard work until commit time and then we
4376 ask the repository), but it breaks some move scenarios.
4379 if (! copyfrom_relpath)
4381 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4383 return SVN_NO_ERROR;
4386 /* Fall through. Install not present node */
4388 case svn_wc__db_status_not_present:
4389 case svn_wc__db_status_excluded:
4390 /* These presence values should not create a new op depth */
4391 if (dst_np_op_depth > 0)
4393 dst_op_depth = dst_np_op_depth;
4394 dst_np_op_depth = -1;
4396 if (status == svn_wc__db_status_excluded)
4397 dst_presence = svn_wc__db_status_excluded;
4399 dst_presence = svn_wc__db_status_not_present;
4401 case svn_wc__db_status_server_excluded:
4402 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4403 _("Cannot copy '%s' excluded by server"),
4404 path_for_error_message(src_wcroot,
4408 /* Perhaps we should allow incomplete to incomplete? We can't
4409 avoid incomplete working nodes as one step in copying a
4410 directory is to add incomplete children. */
4411 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4412 _("Cannot handle status of '%s'"),
4413 path_for_error_message(src_wcroot,
4418 if (kind == svn_node_dir)
4422 SVN_ERR(op_depth_of(&src_op_depth, src_wcroot, src_relpath));
4423 SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
4424 src_op_depth, scratch_pool, scratch_pool));
4429 if (src_wcroot == dst_wcroot)
4431 svn_sqlite__stmt_t *stmt;
4432 const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
4435 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
4436 STMT_INSERT_WORKING_NODE_COPY_FROM));
4438 SVN_ERR(svn_sqlite__bindf(stmt, "issdst",
4439 src_wcroot->wc_id, src_relpath,
4443 presence_map, dst_presence));
4445 if (move_op_depth > 0)
4447 if (relpath_depth(dst_relpath) == move_op_depth)
4449 /* We're moving the root of the move operation.
4451 * When an added node or the op-root of a copy is moved,
4452 * there is no 'moved-from' corresponding to the moved-here
4453 * node. So the net effect is the same as copy+delete.
4454 * Perform a normal copy operation in these cases. */
4455 if (!(status == svn_wc__db_status_added ||
4456 (status == svn_wc__db_status_copied && op_root)))
4457 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4461 svn_sqlite__stmt_t *info_stmt;
4462 svn_boolean_t have_row;
4464 /* We're moving a child along with the root of the move.
4466 * Set moved-here depending on dst_parent, propagating the
4467 * above decision to moved-along children at the same op_depth.
4468 * We can't use scan_addition() to detect moved-here because
4469 * the delete-half of the move might not yet exist. */
4470 SVN_ERR(svn_sqlite__get_statement(&info_stmt, dst_wcroot->sdb,
4471 STMT_SELECT_NODE_INFO));
4472 SVN_ERR(svn_sqlite__bindf(info_stmt, "is", dst_wcroot->wc_id,
4473 dst_parent_relpath));
4474 SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4475 SVN_ERR_ASSERT(have_row);
4476 if (svn_sqlite__column_boolean(info_stmt, 15) &&
4477 dst_op_depth == dst_parent_op_depth)
4479 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4480 SVN_ERR(svn_sqlite__reset(info_stmt));
4484 SVN_ERR(svn_sqlite__reset(info_stmt));
4486 /* If the child has been moved into the tree we're moving,
4487 * keep its moved-here bit set. */
4488 SVN_ERR(svn_sqlite__get_statement(&info_stmt,
4490 STMT_SELECT_NODE_INFO));
4491 SVN_ERR(svn_sqlite__bindf(info_stmt, "is",
4492 dst_wcroot->wc_id, src_relpath));
4493 SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4494 SVN_ERR_ASSERT(have_row);
4495 if (svn_sqlite__column_boolean(info_stmt, 15))
4496 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4497 SVN_ERR(svn_sqlite__reset(info_stmt));
4502 SVN_ERR(svn_sqlite__step_done(stmt));
4504 /* ### Copying changelist is OK for a move but what about a copy? */
4505 SVN_ERR(copy_actual(src_wcroot, src_relpath,
4506 dst_wcroot, dst_relpath, scratch_pool));
4508 if (dst_np_op_depth > 0)
4510 /* We introduce a not-present node at the parent's op_depth to
4511 properly start a new op-depth at our own op_depth. This marks
4512 us as an op_root for commit and allows reverting just this
4515 SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
4517 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
4518 src_wcroot->wc_id, dst_relpath,
4519 dst_np_op_depth, dst_parent_relpath,
4520 copyfrom_id, copyfrom_relpath,
4523 svn_wc__db_status_not_present,
4527 SVN_ERR(svn_sqlite__step_done(stmt));
4529 /* Insert incomplete children, if relevant.
4530 The children are part of the same op and so have the same op_depth.
4531 (The only time we'd want a different depth is during a recursive
4532 simple add, but we never insert children here during a simple add.) */
4533 if (kind == svn_node_dir
4534 && dst_presence == svn_wc__db_status_normal)
4535 SVN_ERR(insert_incomplete_children(
4548 SVN_ERR(cross_db_copy(src_wcroot, src_relpath, dst_wcroot,
4549 dst_relpath, dst_presence, dst_op_depth,
4550 dst_np_op_depth, kind,
4551 children, copyfrom_id, copyfrom_relpath,
4552 copyfrom_rev, scratch_pool));
4555 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items, scratch_pool));
4557 return SVN_NO_ERROR;
4560 /* Baton for passing args to op_copy_txn(). */
4561 struct op_copy_baton
4563 svn_wc__db_wcroot_t *src_wcroot;
4564 const char *src_relpath;
4566 svn_wc__db_wcroot_t *dst_wcroot;
4567 const char *dst_relpath;
4569 const svn_skel_t *work_items;
4571 svn_boolean_t is_move;
4572 const char *dst_op_root_relpath;
4575 /* Helper for svn_wc__db_op_copy().
4577 * Implements svn_sqlite__transaction_callback_t. */
4578 static svn_error_t *
4579 op_copy_txn(void * baton,
4580 svn_sqlite__db_t *sdb,
4581 apr_pool_t *scratch_pool)
4583 struct op_copy_baton *ocb = baton;
4586 if (sdb != ocb->dst_wcroot->sdb)
4588 /* Source and destination databases differ; so also start a lock
4589 in the destination database, by calling ourself in a lock. */
4591 return svn_error_trace(
4592 svn_sqlite__with_lock(ocb->dst_wcroot->sdb,
4593 op_copy_txn, ocb, scratch_pool));
4596 /* From this point we can assume a lock in the src and dst databases */
4599 move_op_depth = relpath_depth(ocb->dst_op_root_relpath);
4603 SVN_ERR(db_op_copy(ocb->src_wcroot, ocb->src_relpath,
4604 ocb->dst_wcroot, ocb->dst_relpath,
4605 ocb->work_items, move_op_depth, scratch_pool));
4607 return SVN_NO_ERROR;
4611 svn_wc__db_op_copy(svn_wc__db_t *db,
4612 const char *src_abspath,
4613 const char *dst_abspath,
4614 const char *dst_op_root_abspath,
4615 svn_boolean_t is_move,
4616 const svn_skel_t *work_items,
4617 apr_pool_t *scratch_pool)
4619 struct op_copy_baton ocb = {0};
4621 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
4622 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
4623 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_op_root_abspath));
4625 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
4626 &ocb.src_relpath, db,
4628 scratch_pool, scratch_pool));
4629 VERIFY_USABLE_WCROOT(ocb.src_wcroot);
4631 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
4634 scratch_pool, scratch_pool));
4635 VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
4637 ocb.work_items = work_items;
4638 ocb.is_move = is_move;
4639 ocb.dst_op_root_relpath = svn_dirent_skip_ancestor(ocb.dst_wcroot->abspath,
4640 dst_op_root_abspath);
4642 /* Call with the sdb in src_wcroot. It might call itself again to
4643 also obtain a lock in dst_wcroot */
4644 SVN_ERR(svn_sqlite__with_lock(ocb.src_wcroot->sdb, op_copy_txn, &ocb,
4647 return SVN_NO_ERROR;
4650 /* The txn body of svn_wc__db_op_handle_move_back */
4651 static svn_error_t *
4652 handle_move_back(svn_boolean_t *moved_back,
4653 svn_wc__db_wcroot_t *wcroot,
4654 const char *local_relpath,
4655 const char *moved_from_relpath,
4656 const svn_skel_t *work_items,
4657 apr_pool_t *scratch_pool)
4659 svn_sqlite__stmt_t *stmt;
4660 svn_wc__db_status_t status;
4661 svn_boolean_t op_root;
4662 svn_boolean_t have_more_work;
4663 int from_op_depth = 0;
4664 svn_boolean_t have_row;
4665 svn_boolean_t different = FALSE;
4667 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
4669 SVN_ERR(svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL, NULL,
4670 NULL, NULL, NULL, NULL, NULL, NULL,
4671 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4672 &op_root, NULL, NULL, NULL,
4673 &have_more_work, NULL,
4674 wcroot, local_relpath,
4675 scratch_pool, scratch_pool));
4677 if (status != svn_wc__db_status_added || !op_root)
4678 return SVN_NO_ERROR;
4680 /* We have two cases here: BASE-move-back and WORKING-move-back */
4682 SVN_ERR(op_depth_of(&from_op_depth, wcroot,
4683 svn_relpath_dirname(local_relpath, scratch_pool)));
4687 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4688 STMT_SELECT_MOVED_BACK));
4690 SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id,
4693 relpath_depth(local_relpath)));
4695 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4697 SVN_ERR_ASSERT(have_row); /* We checked that the node is an op-root */
4700 svn_boolean_t moved_here = svn_sqlite__column_boolean(stmt, 9);
4701 const char *moved_to = svn_sqlite__column_text(stmt, 10, NULL);
4705 || strcmp(moved_to, moved_from_relpath))
4714 svn_wc__db_status_t upper_status;
4715 svn_wc__db_status_t lower_status;
4717 upper_status = svn_sqlite__column_token(stmt, 1, presence_map);
4719 if (svn_sqlite__column_is_null(stmt, 5))
4721 /* No lower layer replaced. */
4722 if (upper_status != svn_wc__db_status_not_present)
4730 lower_status = svn_sqlite__column_token(stmt, 5, presence_map);
4732 if (upper_status != lower_status)
4738 if (upper_status == svn_wc__db_status_not_present
4739 || upper_status == svn_wc__db_status_excluded)
4741 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4742 continue; /* Nothing to check */
4744 else if (upper_status != svn_wc__db_status_normal)
4746 /* Not a normal move. Mixed revision move? */
4752 const char *upper_repos_relpath;
4753 const char *lower_repos_relpath;
4755 upper_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL);
4756 lower_repos_relpath = svn_sqlite__column_text(stmt, 7, NULL);
4758 if (! upper_repos_relpath
4759 || strcmp(upper_repos_relpath, lower_repos_relpath))
4767 svn_revnum_t upper_rev;
4768 svn_revnum_t lower_rev;
4770 upper_rev = svn_sqlite__column_revnum(stmt, 4);
4771 lower_rev = svn_sqlite__column_revnum(stmt, 8);
4773 if (upper_rev != lower_rev)
4781 apr_int64_t upper_repos_id;
4782 apr_int64_t lower_repos_id;
4784 upper_repos_id = svn_sqlite__column_int64(stmt, 2);
4785 lower_repos_id = svn_sqlite__column_int64(stmt, 6);
4787 if (upper_repos_id != lower_repos_id)
4794 /* Check moved_here? */
4796 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4798 SVN_ERR(svn_sqlite__reset(stmt));
4802 /* Ok, we can now safely remove this complete move, because we
4803 determined that it 100% matches the layer below it. */
4805 /* ### We could copy the recorded timestamps from the higher to the
4806 lower layer in an attempt to improve status performance, but
4807 generally these values should be the same anyway as it was
4809 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4810 STMT_DELETE_MOVED_BACK));
4812 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
4814 relpath_depth(local_relpath)));
4816 SVN_ERR(svn_sqlite__step_done(stmt));
4822 return SVN_NO_ERROR;
4826 svn_wc__db_op_handle_move_back(svn_boolean_t *moved_back,
4828 const char *local_abspath,
4829 const char *moved_from_abspath,
4830 const svn_skel_t *work_items,
4831 apr_pool_t *scratch_pool)
4833 svn_wc__db_wcroot_t *wcroot;
4834 const char *local_relpath;
4835 const char *moved_from_relpath;
4836 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4838 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4840 scratch_pool, scratch_pool));
4841 VERIFY_USABLE_WCROOT(wcroot);
4844 *moved_back = FALSE;
4846 moved_from_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
4847 moved_from_abspath);
4849 if (! local_relpath[0]
4850 || !moved_from_relpath)
4852 /* WC-Roots can't be moved */
4853 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
4854 return SVN_NO_ERROR;
4857 SVN_WC__DB_WITH_TXN(handle_move_back(moved_back, wcroot, local_relpath,
4858 moved_from_relpath, work_items,
4862 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
4865 return SVN_NO_ERROR;
4869 /* The recursive implementation of svn_wc__db_op_copy_shadowed_layer.
4871 * A non-zero MOVE_OP_DEPTH implies that the copy operation is part of
4872 * a move, and indicates the op-depth of the move destination op-root. */
4873 static svn_error_t *
4874 db_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot,
4875 const char *src_relpath,
4877 svn_wc__db_wcroot_t *dst_wcroot,
4878 const char *dst_relpath,
4881 apr_int64_t repos_id,
4882 const char *repos_relpath,
4883 svn_revnum_t revision,
4885 apr_pool_t *scratch_pool)
4887 const apr_array_header_t *children;
4888 apr_pool_t *iterpool;
4889 svn_wc__db_status_t status;
4890 svn_node_kind_t kind;
4891 svn_revnum_t node_revision;
4892 const char *node_repos_relpath;
4893 apr_int64_t node_repos_id;
4894 svn_sqlite__stmt_t *stmt;
4895 svn_wc__db_status_t dst_presence;
4900 err = svn_wc__db_depth_get_info(&status, &kind, &node_revision,
4901 &node_repos_relpath, &node_repos_id,
4902 NULL, NULL, NULL, NULL, NULL, NULL,
4904 src_wcroot, src_relpath, src_op_depth,
4905 scratch_pool, scratch_pool);
4909 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
4910 return svn_error_trace(err);
4912 svn_error_clear(err);
4913 return SVN_NO_ERROR; /* There is no shadowed node at src_op_depth */
4917 if (src_op_depth == 0)
4919 /* If the node is switched or has a different revision then its parent
4920 we shouldn't copy it. (We can't as we would have to insert it at
4921 an unshadowed depth) */
4922 if (status == svn_wc__db_status_not_present
4923 || status == svn_wc__db_status_excluded
4924 || status == svn_wc__db_status_server_excluded
4925 || node_revision != revision
4926 || node_repos_id != repos_id
4927 || strcmp(node_repos_relpath, repos_relpath))
4929 /* Add a not-present node in the destination wcroot */
4930 struct insert_working_baton_t iwb;
4931 const char *repos_root_url;
4932 const char *repos_uuid;
4934 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
4935 src_wcroot->sdb, node_repos_id,
4938 SVN_ERR(create_repos_id(&node_repos_id, repos_root_url, repos_uuid,
4939 dst_wcroot->sdb, scratch_pool));
4943 iwb.op_depth = dst_op_depth;
4944 if (status != svn_wc__db_status_excluded)
4945 iwb.presence = svn_wc__db_status_not_present;
4947 iwb.presence = svn_wc__db_status_excluded;
4951 iwb.original_repos_id = node_repos_id;
4952 iwb.original_revnum = node_revision;
4953 iwb.original_repos_relpath = node_repos_relpath;
4955 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
4958 return SVN_NO_ERROR;
4962 iterpool = svn_pool_create(scratch_pool);
4966 case svn_wc__db_status_normal:
4967 case svn_wc__db_status_added:
4968 case svn_wc__db_status_moved_here:
4969 case svn_wc__db_status_copied:
4970 dst_presence = svn_wc__db_status_normal;
4972 case svn_wc__db_status_deleted:
4973 case svn_wc__db_status_not_present:
4974 dst_presence = svn_wc__db_status_not_present;
4976 case svn_wc__db_status_excluded:
4977 dst_presence = svn_wc__db_status_excluded;
4979 case svn_wc__db_status_server_excluded:
4980 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4981 _("Cannot copy '%s' excluded by server"),
4982 path_for_error_message(src_wcroot,
4986 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4987 _("Cannot handle status of '%s'"),
4988 path_for_error_message(src_wcroot,
4993 if (dst_presence == svn_wc__db_status_normal
4994 && src_wcroot == dst_wcroot) /* ### Remove limitation */
4996 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
4997 STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH));
4999 SVN_ERR(svn_sqlite__bindf(stmt, "issdstd",
5000 src_wcroot->wc_id, src_relpath,
5003 svn_relpath_dirname(dst_relpath, iterpool),
5004 presence_map, dst_presence,
5008 if (dst_op_depth == move_op_depth)
5009 SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
5011 SVN_ERR(svn_sqlite__step_done(stmt));
5014 /* And mark it deleted to allow proper shadowing */
5015 struct insert_working_baton_t iwb;
5019 iwb.op_depth = del_op_depth;
5020 iwb.presence = svn_wc__db_status_base_deleted;
5024 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5030 struct insert_working_baton_t iwb;
5031 if (dst_presence == svn_wc__db_status_normal) /* Fallback for multi-db */
5032 dst_presence = svn_wc__db_status_not_present;
5034 /* And mark it deleted to allow proper shadowing */
5038 iwb.op_depth = dst_op_depth;
5039 iwb.presence = dst_presence;
5042 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5046 SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
5047 src_op_depth, scratch_pool, iterpool));
5049 for (i = 0; i < children->nelts; i++)
5051 const char *name = APR_ARRAY_IDX(children, i, const char *);
5052 const char *child_src_relpath;
5053 const char *child_dst_relpath;
5054 const char *child_repos_relpath = NULL;
5056 svn_pool_clear(iterpool);
5057 child_src_relpath = svn_relpath_join(src_relpath, name, iterpool);
5058 child_dst_relpath = svn_relpath_join(dst_relpath, name, iterpool);
5061 child_repos_relpath = svn_relpath_join(repos_relpath, name, iterpool);
5063 SVN_ERR(db_op_copy_shadowed_layer(
5064 src_wcroot, child_src_relpath, src_op_depth,
5065 dst_wcroot, child_dst_relpath, dst_op_depth,
5067 repos_id, child_repos_relpath, revision,
5068 move_op_depth, scratch_pool));
5071 svn_pool_destroy(iterpool);
5073 return SVN_NO_ERROR;
5076 /* Helper for svn_wc__db_op_copy_shadowed_layer().
5078 * Implements svn_sqlite__transaction_callback_t. */
5079 static svn_error_t *
5080 op_copy_shadowed_layer_txn(void *baton,
5081 svn_sqlite__db_t *sdb,
5082 apr_pool_t *scratch_pool)
5084 struct op_copy_baton *ocb = baton;
5085 const char *src_parent_relpath;
5086 const char *dst_parent_relpath;
5090 const char *repos_relpath = NULL;
5091 apr_int64_t repos_id = INVALID_REPOS_ID;
5092 svn_revnum_t revision = SVN_INVALID_REVNUM;
5094 if (sdb != ocb->dst_wcroot->sdb)
5096 /* Source and destination databases differ; so also start a lock
5097 in the destination database, by calling ourself in a lock. */
5099 return svn_error_trace(
5100 svn_sqlite__with_lock(ocb->dst_wcroot->sdb,
5101 op_copy_shadowed_layer_txn,
5102 ocb, scratch_pool));
5105 /* From this point we can assume a lock in the src and dst databases */
5108 /* src_relpath and dst_relpath can't be wcroot as we need their parents */
5109 SVN_ERR_ASSERT(*ocb->src_relpath && *ocb->dst_relpath);
5111 src_parent_relpath = svn_relpath_dirname(ocb->src_relpath, scratch_pool);
5112 dst_parent_relpath = svn_relpath_dirname(ocb->dst_relpath, scratch_pool);
5114 /* src_parent must be status normal or added; get its op-depth */
5115 SVN_ERR(op_depth_of(&src_op_depth, ocb->src_wcroot, src_parent_relpath));
5117 /* dst_parent must be status added; get its op-depth */
5118 SVN_ERR(op_depth_of(&dst_op_depth, ocb->dst_wcroot, dst_parent_relpath));
5120 del_op_depth = relpath_depth(ocb->dst_relpath);
5122 /* Get some information from the parent */
5123 SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, &revision, &repos_relpath,
5124 &repos_id, NULL, NULL, NULL, NULL, NULL,
5127 src_parent_relpath, src_op_depth,
5128 scratch_pool, scratch_pool));
5130 if (repos_relpath == NULL)
5132 /* The node is a local addition and has no shadowed information */
5133 return SVN_NO_ERROR;
5136 /* And calculate the child repos relpath */
5137 repos_relpath = svn_relpath_join(repos_relpath,
5138 svn_relpath_basename(ocb->src_relpath,
5142 SVN_ERR(db_op_copy_shadowed_layer(
5143 ocb->src_wcroot, ocb->src_relpath, src_op_depth,
5144 ocb->dst_wcroot, ocb->dst_relpath, dst_op_depth,
5146 repos_id, repos_relpath, revision,
5147 (ocb->is_move ? dst_op_depth : 0),
5150 return SVN_NO_ERROR;
5154 svn_wc__db_op_copy_shadowed_layer(svn_wc__db_t *db,
5155 const char *src_abspath,
5156 const char *dst_abspath,
5157 svn_boolean_t is_move,
5158 apr_pool_t *scratch_pool)
5160 struct op_copy_baton ocb = {0};
5162 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
5163 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
5165 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
5166 &ocb.src_relpath, db,
5168 scratch_pool, scratch_pool));
5169 VERIFY_USABLE_WCROOT(ocb.src_wcroot);
5171 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
5174 scratch_pool, scratch_pool));
5175 VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
5177 ocb.is_move = is_move;
5178 ocb.dst_op_root_relpath = NULL; /* not used by op_copy_shadowed_layer_txn */
5180 ocb.work_items = NULL;
5182 /* Call with the sdb in src_wcroot. It might call itself again to
5183 also obtain a lock in dst_wcroot */
5184 SVN_ERR(svn_sqlite__with_lock(ocb.src_wcroot->sdb,
5185 op_copy_shadowed_layer_txn,
5186 &ocb, scratch_pool));
5188 return SVN_NO_ERROR;
5192 /* If there are any server-excluded base nodes then the copy must fail
5193 as it's not possible to commit such a copy.
5194 Return an error if there are any server-excluded nodes. */
5195 static svn_error_t *
5196 catch_copy_of_server_excluded(svn_wc__db_wcroot_t *wcroot,
5197 const char *local_relpath,
5198 apr_pool_t *scratch_pool)
5200 svn_sqlite__stmt_t *stmt;
5201 svn_boolean_t have_row;
5202 const char *server_excluded_relpath;
5204 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5205 STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
5206 SVN_ERR(svn_sqlite__bindf(stmt, "is",
5209 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5211 server_excluded_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
5212 SVN_ERR(svn_sqlite__reset(stmt));
5214 return svn_error_createf(SVN_ERR_AUTHZ_UNREADABLE, NULL,
5215 _("Cannot copy '%s' excluded by server"),
5216 path_for_error_message(wcroot,
5217 server_excluded_relpath,
5220 return SVN_NO_ERROR;
5225 svn_wc__db_op_copy_dir(svn_wc__db_t *db,
5226 const char *local_abspath,
5227 const apr_hash_t *props,
5228 svn_revnum_t changed_rev,
5229 apr_time_t changed_date,
5230 const char *changed_author,
5231 const char *original_repos_relpath,
5232 const char *original_root_url,
5233 const char *original_uuid,
5234 svn_revnum_t original_revision,
5235 const apr_array_header_t *children,
5236 svn_boolean_t is_move,
5238 const svn_skel_t *conflict,
5239 const svn_skel_t *work_items,
5240 apr_pool_t *scratch_pool)
5242 svn_wc__db_wcroot_t *wcroot;
5243 const char *local_relpath;
5244 insert_working_baton_t iwb;
5245 int parent_op_depth;
5247 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5248 SVN_ERR_ASSERT(props != NULL);
5249 /* ### any assertions for CHANGED_* ? */
5250 /* ### any assertions for ORIGINAL_* ? */
5252 SVN_ERR_ASSERT(children != NULL);
5255 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5256 local_abspath, scratch_pool, scratch_pool));
5257 VERIFY_USABLE_WCROOT(wcroot);
5261 iwb.presence = svn_wc__db_status_normal;
5262 iwb.kind = svn_node_dir;
5265 iwb.changed_rev = changed_rev;
5266 iwb.changed_date = changed_date;
5267 iwb.changed_author = changed_author;
5269 if (original_root_url != NULL)
5271 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5272 original_root_url, original_uuid,
5273 wcroot->sdb, scratch_pool));
5274 iwb.original_repos_relpath = original_repos_relpath;
5275 iwb.original_revnum = original_revision;
5278 /* ### Should we do this inside the transaction? */
5279 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5280 &parent_op_depth, iwb.original_repos_id,
5281 original_repos_relpath, original_revision,
5282 wcroot, local_relpath, scratch_pool));
5284 iwb.children = children;
5286 iwb.moved_here = is_move && (parent_op_depth == 0 ||
5287 iwb.op_depth == parent_op_depth);
5289 iwb.work_items = work_items;
5290 iwb.conflict = conflict;
5292 SVN_WC__DB_WITH_TXN(
5293 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5295 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
5297 return SVN_NO_ERROR;
5302 svn_wc__db_op_copy_file(svn_wc__db_t *db,
5303 const char *local_abspath,
5304 const apr_hash_t *props,
5305 svn_revnum_t changed_rev,
5306 apr_time_t changed_date,
5307 const char *changed_author,
5308 const char *original_repos_relpath,
5309 const char *original_root_url,
5310 const char *original_uuid,
5311 svn_revnum_t original_revision,
5312 const svn_checksum_t *checksum,
5313 svn_boolean_t update_actual_props,
5314 const apr_hash_t *new_actual_props,
5315 svn_boolean_t is_move,
5316 const svn_skel_t *conflict,
5317 const svn_skel_t *work_items,
5318 apr_pool_t *scratch_pool)
5320 svn_wc__db_wcroot_t *wcroot;
5321 const char *local_relpath;
5322 insert_working_baton_t iwb;
5323 int parent_op_depth;
5325 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5326 SVN_ERR_ASSERT(props != NULL);
5327 /* ### any assertions for CHANGED_* ? */
5328 SVN_ERR_ASSERT((! original_repos_relpath && ! original_root_url
5329 && ! original_uuid && ! checksum
5330 && original_revision == SVN_INVALID_REVNUM)
5331 || (original_repos_relpath && original_root_url
5332 && original_uuid && checksum
5333 && original_revision != SVN_INVALID_REVNUM));
5335 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5336 local_abspath, scratch_pool, scratch_pool));
5337 VERIFY_USABLE_WCROOT(wcroot);
5341 iwb.presence = svn_wc__db_status_normal;
5342 iwb.kind = svn_node_file;
5345 iwb.changed_rev = changed_rev;
5346 iwb.changed_date = changed_date;
5347 iwb.changed_author = changed_author;
5349 if (original_root_url != NULL)
5351 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5352 original_root_url, original_uuid,
5353 wcroot->sdb, scratch_pool));
5354 iwb.original_repos_relpath = original_repos_relpath;
5355 iwb.original_revnum = original_revision;
5358 /* ### Should we do this inside the transaction? */
5359 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5360 &parent_op_depth, iwb.original_repos_id,
5361 original_repos_relpath, original_revision,
5362 wcroot, local_relpath, scratch_pool));
5364 iwb.checksum = checksum;
5365 iwb.moved_here = is_move && (parent_op_depth == 0 ||
5366 iwb.op_depth == parent_op_depth);
5368 if (update_actual_props)
5370 iwb.update_actual_props = update_actual_props;
5371 iwb.new_actual_props = new_actual_props;
5374 iwb.work_items = work_items;
5375 iwb.conflict = conflict;
5377 SVN_WC__DB_WITH_TXN(
5378 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5380 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5382 return SVN_NO_ERROR;
5387 svn_wc__db_op_copy_symlink(svn_wc__db_t *db,
5388 const char *local_abspath,
5389 const apr_hash_t *props,
5390 svn_revnum_t changed_rev,
5391 apr_time_t changed_date,
5392 const char *changed_author,
5393 const char *original_repos_relpath,
5394 const char *original_root_url,
5395 const char *original_uuid,
5396 svn_revnum_t original_revision,
5398 const svn_skel_t *conflict,
5399 const svn_skel_t *work_items,
5400 apr_pool_t *scratch_pool)
5402 svn_wc__db_wcroot_t *wcroot;
5403 const char *local_relpath;
5404 insert_working_baton_t iwb;
5405 int parent_op_depth;
5407 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5408 SVN_ERR_ASSERT(props != NULL);
5409 /* ### any assertions for CHANGED_* ? */
5410 /* ### any assertions for ORIGINAL_* ? */
5411 SVN_ERR_ASSERT(target != NULL);
5413 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5414 local_abspath, scratch_pool, scratch_pool));
5415 VERIFY_USABLE_WCROOT(wcroot);
5419 iwb.presence = svn_wc__db_status_normal;
5420 iwb.kind = svn_node_symlink;
5423 iwb.changed_rev = changed_rev;
5424 iwb.changed_date = changed_date;
5425 iwb.changed_author = changed_author;
5426 iwb.moved_here = FALSE;
5428 if (original_root_url != NULL)
5430 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5431 original_root_url, original_uuid,
5432 wcroot->sdb, scratch_pool));
5433 iwb.original_repos_relpath = original_repos_relpath;
5434 iwb.original_revnum = original_revision;
5437 /* ### Should we do this inside the transaction? */
5438 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5439 &parent_op_depth, iwb.original_repos_id,
5440 original_repos_relpath, original_revision,
5441 wcroot, local_relpath, scratch_pool));
5443 iwb.target = target;
5445 iwb.work_items = work_items;
5446 iwb.conflict = conflict;
5448 SVN_WC__DB_WITH_TXN(
5449 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5451 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5453 return SVN_NO_ERROR;
5458 svn_wc__db_op_add_directory(svn_wc__db_t *db,
5459 const char *local_abspath,
5460 const apr_hash_t *props,
5461 const svn_skel_t *work_items,
5462 apr_pool_t *scratch_pool)
5464 svn_wc__db_wcroot_t *wcroot;
5465 const char *local_relpath;
5466 const char *dir_abspath;
5468 insert_working_baton_t iwb;
5470 /* Resolve wcroot via parent directory to avoid obstruction handling */
5471 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5472 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5474 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5475 dir_abspath, scratch_pool, scratch_pool));
5476 VERIFY_USABLE_WCROOT(wcroot);
5480 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5481 iwb.presence = svn_wc__db_status_normal;
5482 iwb.kind = svn_node_dir;
5483 iwb.op_depth = relpath_depth(local_relpath);
5484 if (props && apr_hash_count((apr_hash_t *)props))
5486 iwb.update_actual_props = TRUE;
5487 iwb.new_actual_props = props;
5490 iwb.work_items = work_items;
5492 SVN_WC__DB_WITH_TXN(
5493 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5495 /* Use depth infinity to make sure we have no invalid cached information
5496 * about children of this dir. */
5497 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5500 return SVN_NO_ERROR;
5505 svn_wc__db_op_add_file(svn_wc__db_t *db,
5506 const char *local_abspath,
5507 const apr_hash_t *props,
5508 const svn_skel_t *work_items,
5509 apr_pool_t *scratch_pool)
5511 svn_wc__db_wcroot_t *wcroot;
5512 const char *local_relpath;
5513 insert_working_baton_t iwb;
5514 const char *dir_abspath;
5517 /* Resolve wcroot via parent directory to avoid obstruction handling */
5518 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5519 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5521 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5522 dir_abspath, scratch_pool, scratch_pool));
5523 VERIFY_USABLE_WCROOT(wcroot);
5527 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5528 iwb.presence = svn_wc__db_status_normal;
5529 iwb.kind = svn_node_file;
5530 iwb.op_depth = relpath_depth(local_relpath);
5531 if (props && apr_hash_count((apr_hash_t *)props))
5533 iwb.update_actual_props = TRUE;
5534 iwb.new_actual_props = props;
5537 iwb.work_items = work_items;
5539 SVN_WC__DB_WITH_TXN(
5540 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5542 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5544 return SVN_NO_ERROR;
5549 svn_wc__db_op_add_symlink(svn_wc__db_t *db,
5550 const char *local_abspath,
5552 const apr_hash_t *props,
5553 const svn_skel_t *work_items,
5554 apr_pool_t *scratch_pool)
5556 svn_wc__db_wcroot_t *wcroot;
5557 const char *local_relpath;
5558 insert_working_baton_t iwb;
5559 const char *dir_abspath;
5562 /* Resolve wcroot via parent directory to avoid obstruction handling */
5563 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5564 SVN_ERR_ASSERT(target != NULL);
5566 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5568 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5569 dir_abspath, scratch_pool, scratch_pool));
5571 VERIFY_USABLE_WCROOT(wcroot);
5575 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5576 iwb.presence = svn_wc__db_status_normal;
5577 iwb.kind = svn_node_symlink;
5578 iwb.op_depth = relpath_depth(local_relpath);
5579 if (props && apr_hash_count((apr_hash_t *)props))
5581 iwb.update_actual_props = TRUE;
5582 iwb.new_actual_props = props;
5585 iwb.target = target;
5587 iwb.work_items = work_items;
5589 SVN_WC__DB_WITH_TXN(
5590 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5592 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5594 return SVN_NO_ERROR;
5597 /* Record RECORDED_SIZE and RECORDED_TIME into top layer in NODES */
5598 static svn_error_t *
5599 db_record_fileinfo(svn_wc__db_wcroot_t *wcroot,
5600 const char *local_relpath,
5601 apr_int64_t recorded_size,
5602 apr_int64_t recorded_time,
5603 apr_pool_t *scratch_pool)
5605 svn_sqlite__stmt_t *stmt;
5608 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5609 STMT_UPDATE_NODE_FILEINFO));
5610 SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
5611 recorded_size, recorded_time));
5612 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
5614 SVN_ERR_ASSERT(affected_rows == 1);
5616 return SVN_NO_ERROR;
5621 svn_wc__db_global_record_fileinfo(svn_wc__db_t *db,
5622 const char *local_abspath,
5623 svn_filesize_t recorded_size,
5624 apr_time_t recorded_time,
5625 apr_pool_t *scratch_pool)
5627 svn_wc__db_wcroot_t *wcroot;
5628 const char *local_relpath;
5630 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5632 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5633 local_abspath, scratch_pool, scratch_pool));
5634 VERIFY_USABLE_WCROOT(wcroot);
5636 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
5637 recorded_size, recorded_time, scratch_pool));
5639 /* We *totally* monkeyed the entries. Toss 'em. */
5640 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5642 return SVN_NO_ERROR;
5646 /* Set the ACTUAL_NODE properties column for (WC_ID, LOCAL_RELPATH) to
5649 * Note: PROPS=NULL means the actual props are the same as the pristine
5650 * props; to indicate no properties when the pristine has some props,
5651 * PROPS must be an empty hash. */
5652 static svn_error_t *
5653 set_actual_props(apr_int64_t wc_id,
5654 const char *local_relpath,
5656 svn_sqlite__db_t *db,
5657 apr_pool_t *scratch_pool)
5659 svn_sqlite__stmt_t *stmt;
5662 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_UPDATE_ACTUAL_PROPS));
5663 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
5664 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
5665 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
5667 if (affected_rows == 1 || !props)
5668 return SVN_NO_ERROR; /* We are done */
5670 /* We have to insert a row in ACTUAL */
5672 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_ACTUAL_PROPS));
5673 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
5674 if (*local_relpath != '\0')
5675 SVN_ERR(svn_sqlite__bind_text(stmt, 3,
5676 svn_relpath_dirname(local_relpath,
5678 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
5679 return svn_error_trace(svn_sqlite__step_done(stmt));
5683 /* The body of svn_wc__db_op_set_props().
5685 Set the 'properties' column in the 'ACTUAL_NODE' table to BATON->props.
5686 Create an entry in the ACTUAL table for the node if it does not yet
5688 To specify no properties, BATON->props must be an empty hash, not NULL.
5689 BATON is of type 'struct set_props_baton_t'.
5691 static svn_error_t *
5692 set_props_txn(svn_wc__db_wcroot_t *wcroot,
5693 const char *local_relpath,
5695 svn_boolean_t clear_recorded_info,
5696 const svn_skel_t *conflict,
5697 const svn_skel_t *work_items,
5698 apr_pool_t *scratch_pool)
5700 apr_hash_t *pristine_props;
5702 /* Check if the props are modified. If no changes, then wipe out the
5703 ACTUAL props. PRISTINE_PROPS==NULL means that any
5704 ACTUAL props are okay as provided, so go ahead and set them. */
5705 SVN_ERR(db_read_pristine_props(&pristine_props, wcroot, local_relpath, FALSE,
5706 scratch_pool, scratch_pool));
5707 if (props && pristine_props)
5709 apr_array_header_t *prop_diffs;
5711 SVN_ERR(svn_prop_diffs(&prop_diffs, props, pristine_props,
5713 if (prop_diffs->nelts == 0)
5717 SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath,
5718 props, wcroot->sdb, scratch_pool));
5720 if (clear_recorded_info)
5722 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
5723 SVN_INVALID_FILESIZE, 0,
5728 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5730 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
5731 conflict, scratch_pool));
5733 return SVN_NO_ERROR;
5738 svn_wc__db_op_set_props(svn_wc__db_t *db,
5739 const char *local_abspath,
5741 svn_boolean_t clear_recorded_info,
5742 const svn_skel_t *conflict,
5743 const svn_skel_t *work_items,
5744 apr_pool_t *scratch_pool)
5746 svn_wc__db_wcroot_t *wcroot;
5747 const char *local_relpath;
5749 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5751 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
5752 db, local_abspath, scratch_pool, scratch_pool));
5753 VERIFY_USABLE_WCROOT(wcroot);
5755 SVN_WC__DB_WITH_TXN(set_props_txn(wcroot, local_relpath, props,
5756 clear_recorded_info, conflict, work_items,
5759 return SVN_NO_ERROR;
5764 svn_wc__db_op_modified(svn_wc__db_t *db,
5765 const char *local_abspath,
5766 apr_pool_t *scratch_pool)
5768 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5774 static svn_error_t *
5775 populate_targets_tree(svn_wc__db_wcroot_t *wcroot,
5776 const char *local_relpath,
5778 const apr_array_header_t *changelist_filter,
5779 apr_pool_t *scratch_pool)
5781 svn_sqlite__stmt_t *stmt;
5782 int affected_rows = 0;
5783 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
5784 STMT_CREATE_TARGETS_LIST));
5786 if (changelist_filter && changelist_filter->nelts > 0)
5788 /* Iterate over the changelists, adding the nodes which match.
5789 Common case: we only have one changelist, so this only
5796 case svn_depth_empty:
5797 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST;
5800 case svn_depth_files:
5801 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES;
5804 case svn_depth_immediates:
5805 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES;
5808 case svn_depth_infinity:
5809 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY;
5813 /* We don't know how to handle unknown or exclude. */
5814 SVN_ERR_MALFUNCTION();
5818 for (i = 0; i < changelist_filter->nelts; i++)
5821 const char *changelist = APR_ARRAY_IDX(changelist_filter, i,
5824 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5825 STMT_INSERT_TARGET_WITH_CHANGELIST));
5826 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
5827 local_relpath, changelist));
5828 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5830 /* If the root is matched by the changelist, we don't have to match
5831 the children. As that tells us the root is a file */
5832 if (!sub_affected && depth > svn_depth_empty)
5834 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
5835 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
5836 local_relpath, changelist));
5837 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5840 affected_rows += sub_affected;
5843 else /* No changelist filtering */
5850 case svn_depth_empty:
5851 stmt_idx = STMT_INSERT_TARGET;
5854 case svn_depth_files:
5855 stmt_idx = STMT_INSERT_TARGET_DEPTH_FILES;
5858 case svn_depth_immediates:
5859 stmt_idx = STMT_INSERT_TARGET_DEPTH_IMMEDIATES;
5862 case svn_depth_infinity:
5863 stmt_idx = STMT_INSERT_TARGET_DEPTH_INFINITY;
5867 /* We don't know how to handle unknown or exclude. */
5868 SVN_ERR_MALFUNCTION();
5872 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5873 STMT_INSERT_TARGET));
5874 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
5875 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5876 affected_rows += sub_affected;
5878 if (depth > svn_depth_empty)
5880 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
5881 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
5882 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5883 affected_rows += sub_affected;
5887 /* Does the target exist? */
5888 if (affected_rows == 0)
5890 svn_boolean_t exists;
5891 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
5894 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
5895 _("The node '%s' was not found."),
5896 path_for_error_message(wcroot,
5901 return SVN_NO_ERROR;
5906 static svn_error_t *
5907 dump_targets(svn_wc__db_wcroot_t *wcroot,
5908 apr_pool_t *scratch_pool)
5910 svn_sqlite__stmt_t *stmt;
5911 svn_boolean_t have_row;
5913 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5914 STMT_SELECT_TARGETS));
5915 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5918 const char *target = svn_sqlite__column_text(stmt, 0, NULL);
5919 SVN_DBG(("Target: '%s'\n", target));
5920 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5923 SVN_ERR(svn_sqlite__reset(stmt));
5925 return SVN_NO_ERROR;
5930 struct set_changelist_baton_t
5932 const char *new_changelist;
5933 const apr_array_header_t *changelist_filter;
5938 /* The main part of svn_wc__db_op_set_changelist().
5940 * Implements svn_wc__db_txn_callback_t. */
5941 static svn_error_t *
5942 set_changelist_txn(void *baton,
5943 svn_wc__db_wcroot_t *wcroot,
5944 const char *local_relpath,
5945 apr_pool_t *scratch_pool)
5947 struct set_changelist_baton_t *scb = baton;
5948 svn_sqlite__stmt_t *stmt;
5950 SVN_ERR(populate_targets_tree(wcroot, local_relpath, scb->depth,
5951 scb->changelist_filter, scratch_pool));
5953 /* Ensure we have actual nodes for our targets. */
5954 if (scb->new_changelist)
5956 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5957 STMT_INSERT_ACTUAL_EMPTIES));
5958 SVN_ERR(svn_sqlite__step_done(stmt));
5961 /* Now create our notification table. */
5962 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
5963 STMT_CREATE_CHANGELIST_LIST));
5964 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
5965 STMT_CREATE_CHANGELIST_TRIGGER));
5967 /* Update our changelists. */
5968 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5969 STMT_UPDATE_ACTUAL_CHANGELISTS));
5970 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
5971 scb->new_changelist));
5972 SVN_ERR(svn_sqlite__step_done(stmt));
5974 if (scb->new_changelist)
5976 /* We have to notify that we skipped directories, so do that now. */
5977 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5978 STMT_MARK_SKIPPED_CHANGELIST_DIRS));
5979 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
5980 scb->new_changelist));
5981 SVN_ERR(svn_sqlite__step_done(stmt));
5984 /* We may have left empty ACTUAL nodes, so remove them. This is only a
5985 potential problem if we removed changelists. */
5986 if (!scb->new_changelist)
5988 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5989 STMT_DELETE_ACTUAL_EMPTIES));
5990 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
5991 SVN_ERR(svn_sqlite__step_done(stmt));
5994 return SVN_NO_ERROR;
5998 /* Send notifications for svn_wc__db_op_set_changelist().
6000 * Implements work_callback_t. */
6001 static svn_error_t *
6002 do_changelist_notify(void *baton,
6003 svn_wc__db_wcroot_t *wcroot,
6004 svn_cancel_func_t cancel_func,
6006 svn_wc_notify_func2_t notify_func,
6008 apr_pool_t *scratch_pool)
6010 svn_sqlite__stmt_t *stmt;
6011 svn_boolean_t have_row;
6012 apr_pool_t *iterpool;
6014 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6015 STMT_SELECT_CHANGELIST_LIST));
6016 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6018 iterpool = svn_pool_create(scratch_pool);
6021 /* ### wc_id is column 0. use it one day... */
6022 const char *notify_relpath = svn_sqlite__column_text(stmt, 1, NULL);
6023 svn_wc_notify_action_t action = svn_sqlite__column_int(stmt, 2);
6024 svn_wc_notify_t *notify;
6025 const char *notify_abspath;
6027 svn_pool_clear(iterpool);
6031 svn_error_t *err = cancel_func(cancel_baton);
6034 return svn_error_trace(svn_error_compose_create(
6036 svn_sqlite__reset(stmt)));
6039 notify_abspath = svn_dirent_join(wcroot->abspath, notify_relpath,
6041 notify = svn_wc_create_notify(notify_abspath, action, iterpool);
6042 notify->changelist_name = svn_sqlite__column_text(stmt, 3, NULL);
6043 notify_func(notify_baton, notify, iterpool);
6045 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6047 svn_pool_destroy(iterpool);
6049 return svn_error_trace(svn_sqlite__reset(stmt));
6054 svn_wc__db_op_set_changelist(svn_wc__db_t *db,
6055 const char *local_abspath,
6056 const char *new_changelist,
6057 const apr_array_header_t *changelist_filter,
6059 svn_wc_notify_func2_t notify_func,
6061 svn_cancel_func_t cancel_func,
6063 apr_pool_t *scratch_pool)
6065 svn_wc__db_wcroot_t *wcroot;
6066 const char *local_relpath;
6067 struct set_changelist_baton_t scb;
6069 scb.new_changelist = new_changelist;
6070 scb.changelist_filter = changelist_filter;
6073 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6075 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6077 scratch_pool, scratch_pool));
6078 VERIFY_USABLE_WCROOT(wcroot);
6080 /* Flush the entries before we do the work. Even if no work is performed,
6081 the flush isn't a problem. */
6082 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6084 /* Perform the set-changelist operation (transactionally), perform any
6085 notifications necessary, and then clean out our temporary tables. */
6086 return svn_error_trace(with_finalization(wcroot, local_relpath,
6087 set_changelist_txn, &scb,
6088 do_changelist_notify, NULL,
6089 cancel_func, cancel_baton,
6090 notify_func, notify_baton,
6091 STMT_FINALIZE_CHANGELIST,
6095 /* Implementation of svn_wc__db_op_mark_conflict() */
6097 svn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t *wcroot,
6098 const char *local_relpath,
6099 const svn_skel_t *conflict_skel,
6100 apr_pool_t *scratch_pool)
6102 svn_sqlite__stmt_t *stmt;
6103 svn_boolean_t got_row;
6104 svn_boolean_t is_complete;
6106 SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict_skel));
6107 SVN_ERR_ASSERT(is_complete);
6109 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6110 STMT_SELECT_ACTUAL_NODE));
6111 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6112 SVN_ERR(svn_sqlite__step(&got_row, stmt));
6113 SVN_ERR(svn_sqlite__reset(stmt));
6117 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6118 STMT_UPDATE_ACTUAL_CONFLICT));
6119 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6123 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6124 STMT_INSERT_ACTUAL_CONFLICT));
6125 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6126 if (*local_relpath != '\0')
6127 SVN_ERR(svn_sqlite__bind_text(stmt, 4,
6128 svn_relpath_dirname(local_relpath,
6133 svn_stringbuf_t *sb = svn_skel__unparse(conflict_skel, scratch_pool);
6135 SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6138 SVN_ERR(svn_sqlite__update(NULL, stmt));
6140 return SVN_NO_ERROR;
6144 svn_wc__db_op_mark_conflict(svn_wc__db_t *db,
6145 const char *local_abspath,
6146 const svn_skel_t *conflict_skel,
6147 const svn_skel_t *work_items,
6148 apr_pool_t *scratch_pool)
6150 svn_wc__db_wcroot_t *wcroot;
6151 const char *local_relpath;
6153 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6155 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6156 local_abspath, scratch_pool, scratch_pool));
6157 VERIFY_USABLE_WCROOT(wcroot);
6159 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6160 conflict_skel, scratch_pool));
6162 /* ### Should be handled in the same transaction as setting the conflict */
6164 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6166 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6168 return SVN_NO_ERROR;
6172 /* The body of svn_wc__db_op_mark_resolved().
6174 static svn_error_t *
6175 db_op_mark_resolved(svn_wc__db_wcroot_t *wcroot,
6176 const char *local_relpath,
6178 svn_boolean_t resolved_text,
6179 svn_boolean_t resolved_props,
6180 svn_boolean_t resolved_tree,
6181 const svn_skel_t *work_items,
6182 apr_pool_t *scratch_pool)
6184 svn_sqlite__stmt_t *stmt;
6185 svn_boolean_t have_row;
6186 int total_affected_rows = 0;
6187 svn_boolean_t resolved_all;
6188 apr_size_t conflict_len;
6189 const void *conflict_data;
6190 svn_skel_t *conflicts;
6192 /* Check if we have a conflict in ACTUAL */
6193 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6194 STMT_SELECT_ACTUAL_NODE));
6195 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6197 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6201 SVN_ERR(svn_sqlite__reset(stmt));
6203 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6204 STMT_SELECT_NODE_INFO));
6206 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6208 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6209 SVN_ERR(svn_sqlite__reset(stmt));
6212 return SVN_NO_ERROR;
6214 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6215 _("The node '%s' was not found."),
6216 path_for_error_message(wcroot,
6221 conflict_data = svn_sqlite__column_blob(stmt, 2, &conflict_len,
6223 conflicts = svn_skel__parse(conflict_data, conflict_len, scratch_pool);
6224 SVN_ERR(svn_sqlite__reset(stmt));
6226 SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
6227 db, wcroot->abspath,
6229 resolved_props ? "" : NULL,
6231 scratch_pool, scratch_pool));
6233 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6234 STMT_UPDATE_ACTUAL_CONFLICT));
6235 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6239 svn_stringbuf_t *sb = svn_skel__unparse(conflicts, scratch_pool);
6241 SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6244 SVN_ERR(svn_sqlite__update(&total_affected_rows, stmt));
6246 /* Now, remove the actual node if it doesn't have any more useful
6247 information. We only need to do this if we've remove data ourselves. */
6248 if (total_affected_rows > 0)
6250 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6251 STMT_DELETE_ACTUAL_EMPTY));
6252 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6253 SVN_ERR(svn_sqlite__step_done(stmt));
6256 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6258 return SVN_NO_ERROR;
6262 svn_wc__db_op_mark_resolved(svn_wc__db_t *db,
6263 const char *local_abspath,
6264 svn_boolean_t resolved_text,
6265 svn_boolean_t resolved_props,
6266 svn_boolean_t resolved_tree,
6267 const svn_skel_t *work_items,
6268 apr_pool_t *scratch_pool)
6270 svn_wc__db_wcroot_t *wcroot;
6271 const char *local_relpath;
6273 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6275 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6276 local_abspath, scratch_pool, scratch_pool));
6277 VERIFY_USABLE_WCROOT(wcroot);
6279 SVN_WC__DB_WITH_TXN(
6280 db_op_mark_resolved(wcroot, local_relpath, db,
6281 resolved_text, resolved_props, resolved_tree,
6282 work_items, scratch_pool),
6285 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6286 return SVN_NO_ERROR;
6289 /* Clear moved-to information at the delete-half of the move which
6290 * moved LOCAL_RELPATH here. This transforms the move into a simple delete. */
6291 static svn_error_t *
6292 clear_moved_to(const char *local_relpath,
6293 svn_wc__db_wcroot_t *wcroot,
6294 apr_pool_t *scratch_pool)
6296 svn_sqlite__stmt_t *stmt;
6297 svn_boolean_t have_row;
6298 const char *moved_from_relpath;
6300 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6301 STMT_SELECT_MOVED_FROM_RELPATH));
6302 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6303 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6306 SVN_ERR(svn_sqlite__reset(stmt));
6307 return SVN_NO_ERROR;
6310 moved_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
6311 SVN_ERR(svn_sqlite__reset(stmt));
6313 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6314 STMT_CLEAR_MOVED_TO_RELPATH));
6315 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6317 relpath_depth(moved_from_relpath)));
6318 SVN_ERR(svn_sqlite__step_done(stmt));
6320 return SVN_NO_ERROR;
6323 /* One of the two alternative bodies of svn_wc__db_op_revert().
6325 * Implements svn_wc__db_txn_callback_t. */
6326 static svn_error_t *
6327 op_revert_txn(void *baton,
6328 svn_wc__db_wcroot_t *wcroot,
6329 const char *local_relpath,
6330 apr_pool_t *scratch_pool)
6332 svn_wc__db_t *db = baton;
6333 svn_sqlite__stmt_t *stmt;
6334 svn_boolean_t have_row;
6336 svn_boolean_t moved_here;
6338 const char *moved_to;
6340 /* ### Similar structure to op_revert_recursive_txn, should they be
6343 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6344 STMT_SELECT_NODE_INFO));
6345 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6346 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6349 SVN_ERR(svn_sqlite__reset(stmt));
6351 /* There was no NODE row, so attempt to delete an ACTUAL_NODE row. */
6352 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6353 STMT_DELETE_ACTUAL_NODE));
6354 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6355 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6358 /* Can't do non-recursive actual-only revert if actual-only
6359 children exist. Raise an error to cancel the transaction. */
6360 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6361 STMT_ACTUAL_HAS_CHILDREN));
6362 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6363 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6364 SVN_ERR(svn_sqlite__reset(stmt));
6366 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6367 _("Can't revert '%s' without"
6368 " reverting children"),
6369 path_for_error_message(wcroot,
6372 return SVN_NO_ERROR;
6375 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6376 _("The node '%s' was not found."),
6377 path_for_error_message(wcroot,
6382 op_depth = svn_sqlite__column_int(stmt, 0);
6383 moved_here = svn_sqlite__column_boolean(stmt, 15);
6384 moved_to = svn_sqlite__column_text(stmt, 17, scratch_pool);
6385 SVN_ERR(svn_sqlite__reset(stmt));
6389 SVN_ERR(svn_wc__db_resolve_break_moved_away_internal(wcroot,
6395 svn_skel_t *conflict;
6397 SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, wcroot,
6399 scratch_pool, scratch_pool));
6402 svn_wc_operation_t operation;
6403 svn_boolean_t tree_conflicted;
6405 SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
6407 db, wcroot->abspath,
6409 scratch_pool, scratch_pool));
6411 && (operation == svn_wc_operation_update
6412 || operation == svn_wc_operation_switch))
6414 svn_wc_conflict_reason_t reason;
6415 svn_wc_conflict_action_t action;
6417 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
6419 db, wcroot->abspath,
6424 if (reason == svn_wc_conflict_reason_deleted)
6425 SVN_ERR(svn_wc__db_resolve_delete_raise_moved_away(
6426 db, svn_dirent_join(wcroot->abspath, local_relpath,
6428 NULL, NULL /* ### How do we notify this? */,
6434 if (op_depth > 0 && op_depth == relpath_depth(local_relpath))
6436 /* Can't do non-recursive revert if children exist */
6437 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6438 STMT_SELECT_GE_OP_DEPTH_CHILDREN));
6439 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6440 local_relpath, op_depth));
6441 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6442 SVN_ERR(svn_sqlite__reset(stmt));
6444 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6445 _("Can't revert '%s' without"
6446 " reverting children"),
6447 path_for_error_message(wcroot,
6451 /* Rewrite the op-depth of all deleted children making the
6452 direct children into roots of deletes. */
6453 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6454 STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE));
6455 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6458 SVN_ERR(svn_sqlite__step_done(stmt));
6460 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6461 STMT_DELETE_WORKING_NODE));
6462 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6463 SVN_ERR(svn_sqlite__step_done(stmt));
6465 /* ### This removes the lock, but what about the access baton? */
6466 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6467 STMT_DELETE_WC_LOCK_ORPHAN));
6468 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6469 SVN_ERR(svn_sqlite__step_done(stmt));
6471 /* If this node was moved-here, clear moved-to at the move source. */
6473 SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool));
6476 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6477 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST));
6478 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6479 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6482 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6483 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST));
6484 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6485 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6488 return SVN_NO_ERROR;
6492 /* One of the two alternative bodies of svn_wc__db_op_revert().
6494 * Implements svn_wc__db_txn_callback_t. */
6495 static svn_error_t *
6496 op_revert_recursive_txn(void *baton,
6497 svn_wc__db_wcroot_t *wcroot,
6498 const char *local_relpath,
6499 apr_pool_t *scratch_pool)
6501 svn_sqlite__stmt_t *stmt;
6502 svn_boolean_t have_row;
6504 int select_op_depth;
6505 svn_boolean_t moved_here;
6507 apr_pool_t *iterpool;
6509 /* ### Similar structure to op_revert_txn, should they be
6512 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6513 STMT_SELECT_NODE_INFO));
6514 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6515 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6518 SVN_ERR(svn_sqlite__reset(stmt));
6520 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6521 STMT_DELETE_ACTUAL_NODE));
6522 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
6524 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6527 return SVN_NO_ERROR; /* actual-only revert */
6529 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6530 _("The node '%s' was not found."),
6531 path_for_error_message(wcroot,
6536 op_depth = svn_sqlite__column_int(stmt, 0);
6537 moved_here = svn_sqlite__column_boolean(stmt, 15);
6538 SVN_ERR(svn_sqlite__reset(stmt));
6540 if (op_depth > 0 && op_depth != relpath_depth(local_relpath))
6541 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6542 _("Can't revert '%s' without"
6543 " reverting parent"),
6544 path_for_error_message(wcroot,
6548 /* Remove moved-here from move destinations outside the tree. */
6549 SVN_ERR(svn_sqlite__get_statement(
6550 &stmt, wcroot->sdb, STMT_SELECT_MOVED_OUTSIDE));
6551 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
6553 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6556 const char *move_src_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6559 err = svn_wc__db_resolve_break_moved_away_internal(wcroot,
6563 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
6565 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6567 SVN_ERR(svn_sqlite__reset(stmt));
6569 /* Don't delete BASE nodes */
6570 select_op_depth = op_depth ? op_depth : 1;
6572 /* Reverting any non wc-root node */
6573 SVN_ERR(svn_sqlite__get_statement(
6575 STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
6576 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6577 local_relpath, select_op_depth));
6578 SVN_ERR(svn_sqlite__step_done(stmt));
6580 SVN_ERR(svn_sqlite__get_statement(
6582 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
6583 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6584 SVN_ERR(svn_sqlite__step_done(stmt));
6586 SVN_ERR(svn_sqlite__get_statement(
6588 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
6589 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6590 SVN_ERR(svn_sqlite__step_done(stmt));
6592 /* ### This removes the locks, but what about the access batons? */
6593 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6594 STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
6595 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
6597 SVN_ERR(svn_sqlite__step_done(stmt));
6599 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6600 STMT_SELECT_MOVED_HERE_CHILDREN));
6601 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6603 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6605 iterpool = svn_pool_create(scratch_pool);
6608 const char *moved_here_child_relpath;
6611 svn_pool_clear(iterpool);
6613 moved_here_child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
6614 err = clear_moved_to(moved_here_child_relpath, wcroot, iterpool);
6616 return svn_error_trace(svn_error_compose_create(
6618 svn_sqlite__reset(stmt)));
6620 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6622 SVN_ERR(svn_sqlite__reset(stmt));
6623 svn_pool_destroy(iterpool);
6625 /* Clear potential moved-to pointing at the target node itself. */
6626 if (op_depth > 0 && op_depth == relpath_depth(local_relpath)
6628 SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool));
6630 return SVN_NO_ERROR;
6634 svn_wc__db_op_revert(svn_wc__db_t *db,
6635 const char *local_abspath,
6637 apr_pool_t *result_pool,
6638 apr_pool_t *scratch_pool)
6640 svn_wc__db_wcroot_t *wcroot;
6641 const char *local_relpath;
6642 struct with_triggers_baton_t wtb = { STMT_CREATE_REVERT_LIST,
6643 STMT_DROP_REVERT_LIST_TRIGGERS,
6646 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6650 case svn_depth_empty:
6651 wtb.cb_func = op_revert_txn;
6654 case svn_depth_infinity:
6655 wtb.cb_func = op_revert_recursive_txn;
6658 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
6659 _("Unsupported depth for revert of '%s'"),
6660 svn_dirent_local_style(local_abspath,
6664 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6665 db, local_abspath, scratch_pool, scratch_pool));
6666 VERIFY_USABLE_WCROOT(wcroot);
6668 SVN_WC__DB_WITH_TXN(with_triggers(&wtb, wcroot, local_relpath, scratch_pool),
6671 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6673 return SVN_NO_ERROR;
6676 /* The body of svn_wc__db_revert_list_read().
6678 static svn_error_t *
6679 revert_list_read(svn_boolean_t *reverted,
6680 const apr_array_header_t **marker_paths,
6681 svn_boolean_t *copied_here,
6682 svn_node_kind_t *kind,
6683 svn_wc__db_wcroot_t *wcroot,
6684 const char *local_relpath,
6686 apr_pool_t *result_pool,
6687 apr_pool_t *scratch_pool)
6689 svn_sqlite__stmt_t *stmt;
6690 svn_boolean_t have_row;
6693 *marker_paths = NULL;
6694 *copied_here = FALSE;
6695 *kind = svn_node_unknown;
6697 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6698 STMT_SELECT_REVERT_LIST));
6699 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6700 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6703 svn_boolean_t is_actual = svn_sqlite__column_boolean(stmt, 0);
6704 svn_boolean_t another_row = FALSE;
6708 apr_size_t conflict_len;
6709 const void *conflict_data;
6711 conflict_data = svn_sqlite__column_blob(stmt, 5, &conflict_len,
6715 svn_skel_t *conflicts = svn_skel__parse(conflict_data,
6719 SVN_ERR(svn_wc__conflict_read_markers(marker_paths,
6720 db, wcroot->abspath,
6726 if (!svn_sqlite__column_is_null(stmt, 1)) /* notify */
6729 SVN_ERR(svn_sqlite__step(&another_row, stmt));
6732 if (!is_actual || another_row)
6735 if (!svn_sqlite__column_is_null(stmt, 4)) /* repos_id */
6737 int op_depth = svn_sqlite__column_int(stmt, 3);
6738 *copied_here = (op_depth == relpath_depth(local_relpath));
6740 *kind = svn_sqlite__column_token(stmt, 2, kind_map);
6744 SVN_ERR(svn_sqlite__reset(stmt));
6748 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6749 STMT_DELETE_REVERT_LIST));
6750 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6751 SVN_ERR(svn_sqlite__step_done(stmt));
6754 return SVN_NO_ERROR;
6758 svn_wc__db_revert_list_read(svn_boolean_t *reverted,
6759 const apr_array_header_t **marker_files,
6760 svn_boolean_t *copied_here,
6761 svn_node_kind_t *kind,
6763 const char *local_abspath,
6764 apr_pool_t *result_pool,
6765 apr_pool_t *scratch_pool)
6767 svn_wc__db_wcroot_t *wcroot;
6768 const char *local_relpath;
6770 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6771 db, local_abspath, scratch_pool, scratch_pool));
6772 VERIFY_USABLE_WCROOT(wcroot);
6774 SVN_WC__DB_WITH_TXN(
6775 revert_list_read(reverted, marker_files, copied_here, kind,
6776 wcroot, local_relpath, db,
6777 result_pool, scratch_pool),
6779 return SVN_NO_ERROR;
6783 /* The body of svn_wc__db_revert_list_read_copied_children().
6785 static svn_error_t *
6786 revert_list_read_copied_children(svn_wc__db_wcroot_t *wcroot,
6787 const char *local_relpath,
6788 const apr_array_header_t **children_p,
6789 apr_pool_t *result_pool,
6790 apr_pool_t *scratch_pool)
6792 svn_sqlite__stmt_t *stmt;
6793 svn_boolean_t have_row;
6794 apr_array_header_t *children;
6797 apr_array_make(result_pool, 0,
6798 sizeof(svn_wc__db_revert_list_copied_child_info_t *));
6800 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6801 STMT_SELECT_REVERT_LIST_COPIED_CHILDREN));
6802 SVN_ERR(svn_sqlite__bindf(stmt, "sd",
6803 local_relpath, relpath_depth(local_relpath)));
6804 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6807 svn_wc__db_revert_list_copied_child_info_t *child_info;
6808 const char *child_relpath;
6810 child_info = apr_palloc(result_pool, sizeof(*child_info));
6812 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6813 child_info->abspath = svn_dirent_join(wcroot->abspath, child_relpath,
6815 child_info->kind = svn_sqlite__column_token(stmt, 1, kind_map);
6818 svn_wc__db_revert_list_copied_child_info_t *) = child_info;
6820 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6822 SVN_ERR(svn_sqlite__reset(stmt));
6824 *children_p = children;
6826 return SVN_NO_ERROR;
6831 svn_wc__db_revert_list_read_copied_children(const apr_array_header_t **children,
6833 const char *local_abspath,
6834 apr_pool_t *result_pool,
6835 apr_pool_t *scratch_pool)
6837 svn_wc__db_wcroot_t *wcroot;
6838 const char *local_relpath;
6840 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6841 db, local_abspath, scratch_pool, scratch_pool));
6842 VERIFY_USABLE_WCROOT(wcroot);
6844 SVN_WC__DB_WITH_TXN(
6845 revert_list_read_copied_children(wcroot, local_relpath, children,
6846 result_pool, scratch_pool),
6848 return SVN_NO_ERROR;
6853 svn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func,
6856 const char *local_abspath,
6857 apr_pool_t *scratch_pool)
6859 svn_wc__db_wcroot_t *wcroot;
6860 const char *local_relpath;
6861 svn_sqlite__stmt_t *stmt;
6862 svn_boolean_t have_row;
6863 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
6865 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6866 db, local_abspath, scratch_pool, iterpool));
6867 VERIFY_USABLE_WCROOT(wcroot);
6869 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6870 STMT_SELECT_REVERT_LIST_RECURSIVE));
6871 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6872 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6874 return svn_error_trace(svn_sqlite__reset(stmt)); /* optimise for no row */
6877 const char *notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6879 svn_pool_clear(iterpool);
6881 notify_func(notify_baton,
6882 svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
6885 svn_wc_notify_revert,
6889 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6891 SVN_ERR(svn_sqlite__reset(stmt));
6893 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6894 STMT_DELETE_REVERT_LIST_RECURSIVE));
6895 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6896 SVN_ERR(svn_sqlite__step_done(stmt));
6898 svn_pool_destroy(iterpool);
6900 return SVN_NO_ERROR;
6904 svn_wc__db_revert_list_done(svn_wc__db_t *db,
6905 const char *local_abspath,
6906 apr_pool_t *scratch_pool)
6908 svn_wc__db_wcroot_t *wcroot;
6909 const char *local_relpath;
6911 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6912 db, local_abspath, scratch_pool, scratch_pool));
6913 VERIFY_USABLE_WCROOT(wcroot);
6915 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_DROP_REVERT_LIST));
6917 return SVN_NO_ERROR;
6920 /* The body of svn_wc__db_op_remove_node().
6922 static svn_error_t *
6923 remove_node_txn(svn_boolean_t *left_changes,
6924 svn_wc__db_wcroot_t *wcroot,
6925 const char *local_relpath,
6927 svn_boolean_t destroy_wc,
6928 svn_boolean_t destroy_changes,
6929 svn_revnum_t not_present_rev,
6930 svn_wc__db_status_t not_present_status,
6931 svn_node_kind_t not_present_kind,
6932 const svn_skel_t *conflict,
6933 const svn_skel_t *work_items,
6934 svn_cancel_func_t cancel_func,
6936 apr_pool_t *scratch_pool)
6938 svn_sqlite__stmt_t *stmt;
6940 apr_int64_t repos_id;
6941 const char *repos_relpath;
6943 /* Note that unlike many similar functions it is a valid scenario for this
6944 function to be called on a wcroot! */
6946 /* db set when destroying wc */
6947 SVN_ERR_ASSERT(!destroy_wc || db != NULL);
6950 *left_changes = FALSE;
6952 /* Need info for not_present node? */
6953 if (SVN_IS_VALID_REVNUM(not_present_rev))
6954 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
6955 &repos_relpath, &repos_id,
6956 NULL, NULL, NULL, NULL, NULL,
6957 NULL, NULL, NULL, NULL, NULL,
6958 wcroot, local_relpath,
6959 scratch_pool, scratch_pool));
6962 && (!destroy_changes || *local_relpath == '\0'))
6964 svn_boolean_t have_row;
6965 apr_pool_t *iterpool;
6966 svn_error_t *err = NULL;
6968 /* Install WQ items for deleting the unmodified files and all dirs */
6969 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6970 STMT_SELECT_WORKING_PRESENT));
6971 SVN_ERR(svn_sqlite__bindf(stmt, "is",
6972 wcroot->wc_id, local_relpath));
6974 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6976 iterpool = svn_pool_create(scratch_pool);
6980 const char *child_relpath;
6981 const char *child_abspath;
6982 svn_node_kind_t child_kind;
6983 svn_boolean_t have_checksum;
6984 svn_filesize_t recorded_size;
6985 apr_int64_t recorded_time;
6986 const svn_io_dirent2_t *dirent;
6987 svn_boolean_t modified_p = TRUE;
6988 svn_skel_t *work_item = NULL;
6990 svn_pool_clear(iterpool);
6992 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6993 child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
6995 child_abspath = svn_dirent_join(wcroot->abspath, child_relpath,
6998 if (child_kind == svn_node_file)
7000 have_checksum = !svn_sqlite__column_is_null(stmt, 2);
7001 recorded_size = get_recorded_size(stmt, 3);
7002 recorded_time = svn_sqlite__column_int64(stmt, 4);
7006 err = cancel_func(cancel_baton);
7011 err = svn_io_stat_dirent2(&dirent, child_abspath, FALSE, TRUE,
7012 iterpool, iterpool);
7018 || dirent->kind != svn_node_file
7019 || child_kind != svn_node_file)
7021 /* Not interested in keeping changes */
7024 else if (child_kind == svn_node_file
7025 && dirent->kind == svn_node_file
7026 && dirent->filesize == recorded_size
7027 && dirent->mtime == recorded_time)
7029 modified_p = FALSE; /* File matches recorded state */
7031 else if (have_checksum)
7032 err = svn_wc__internal_file_modified_p(&modified_p,
7042 *left_changes = TRUE;
7044 else if (child_kind == svn_node_dir)
7046 err = svn_wc__wq_build_dir_remove(&work_item,
7047 db, wcroot->abspath,
7048 child_abspath, FALSE,
7049 iterpool, iterpool);
7051 else /* svn_node_file || svn_node_symlink */
7053 err = svn_wc__wq_build_file_remove(&work_item,
7054 db, wcroot->abspath,
7056 iterpool, iterpool);
7064 err = add_work_items(wcroot->sdb, work_item, iterpool);
7069 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7071 svn_pool_destroy(iterpool);
7073 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
7076 if (destroy_wc && *local_relpath != '\0')
7078 /* Create work item for destroying the root */
7079 svn_wc__db_status_t status;
7080 svn_node_kind_t kind;
7081 SVN_ERR(read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, NULL,
7082 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7083 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7084 wcroot, local_relpath,
7085 scratch_pool, scratch_pool));
7087 if (status == svn_wc__db_status_normal
7088 || status == svn_wc__db_status_added
7089 || status == svn_wc__db_status_incomplete)
7091 svn_skel_t *work_item = NULL;
7092 const char *local_abspath = svn_dirent_join(wcroot->abspath,
7096 if (kind == svn_node_dir)
7098 SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
7099 db, wcroot->abspath,
7103 scratch_pool, scratch_pool));
7107 svn_boolean_t modified_p = FALSE;
7109 if (!destroy_changes)
7111 SVN_ERR(svn_wc__internal_file_modified_p(&modified_p,
7118 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
7119 db, wcroot->abspath,
7126 *left_changes = TRUE;
7130 SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
7134 /* Remove all nodes below local_relpath */
7135 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7136 STMT_DELETE_NODE_RECURSIVE));
7137 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7138 SVN_ERR(svn_sqlite__step_done(stmt));
7140 /* Delete the root NODE when this is not the working copy root */
7141 if (local_relpath[0] != '\0')
7143 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7145 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7146 SVN_ERR(svn_sqlite__step_done(stmt));
7149 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7150 STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7152 /* Delete all actual nodes at or below local_relpath */
7153 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7155 SVN_ERR(svn_sqlite__step_done(stmt));
7157 /* Should we leave a not-present node? */
7158 if (SVN_IS_VALID_REVNUM(not_present_rev))
7160 insert_base_baton_t ibb;
7163 ibb.repos_id = repos_id;
7165 SVN_ERR_ASSERT(not_present_status == svn_wc__db_status_not_present
7166 || not_present_status == svn_wc__db_status_excluded);
7168 ibb.status = not_present_status;
7169 ibb.kind = not_present_kind;
7171 ibb.repos_relpath = repos_relpath;
7172 ibb.revision = not_present_rev;
7174 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
7177 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
7179 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
7180 conflict, scratch_pool));
7182 return SVN_NO_ERROR;
7186 svn_wc__db_op_remove_node(svn_boolean_t *left_changes,
7188 const char *local_abspath,
7189 svn_boolean_t destroy_wc,
7190 svn_boolean_t destroy_changes,
7191 svn_revnum_t not_present_revision,
7192 svn_wc__db_status_t not_present_status,
7193 svn_node_kind_t not_present_kind,
7194 const svn_skel_t *conflict,
7195 const svn_skel_t *work_items,
7196 svn_cancel_func_t cancel_func,
7198 apr_pool_t *scratch_pool)
7200 svn_wc__db_wcroot_t *wcroot;
7201 const char *local_relpath;
7203 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7205 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7206 local_abspath, scratch_pool, scratch_pool));
7207 VERIFY_USABLE_WCROOT(wcroot);
7209 SVN_WC__DB_WITH_TXN(remove_node_txn(left_changes,
7210 wcroot, local_relpath, db,
7211 destroy_wc, destroy_changes,
7212 not_present_revision, not_present_status,
7213 not_present_kind, conflict, work_items,
7214 cancel_func, cancel_baton, scratch_pool),
7217 /* Flush everything below this node in all ways */
7218 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
7221 return SVN_NO_ERROR;
7225 /* The body of svn_wc__db_op_set_base_depth().
7227 static svn_error_t *
7228 db_op_set_base_depth(svn_wc__db_wcroot_t *wcroot,
7229 const char *local_relpath,
7231 apr_pool_t *scratch_pool)
7233 svn_sqlite__stmt_t *stmt;
7236 /* Flush any entries before we start monkeying the database. */
7237 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7238 STMT_UPDATE_NODE_BASE_DEPTH));
7239 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
7240 svn_token__to_word(depth_map, depth)));
7241 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7243 if (affected_rows == 0)
7244 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7245 "The node '%s' is not a committed directory",
7246 path_for_error_message(wcroot, local_relpath,
7249 return SVN_NO_ERROR;
7254 svn_wc__db_op_set_base_depth(svn_wc__db_t *db,
7255 const char *local_abspath,
7257 apr_pool_t *scratch_pool)
7259 svn_wc__db_wcroot_t *wcroot;
7260 const char *local_relpath;
7262 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7263 SVN_ERR_ASSERT(depth >= svn_depth_empty && depth <= svn_depth_infinity);
7265 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7266 local_abspath, scratch_pool, scratch_pool));
7267 VERIFY_USABLE_WCROOT(wcroot);
7269 /* ### We set depth on working and base to match entry behavior.
7270 Maybe these should be separated later? */
7271 SVN_WC__DB_WITH_TXN(db_op_set_base_depth(wcroot, local_relpath, depth,
7275 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
7277 return SVN_NO_ERROR;
7281 static svn_error_t *
7282 info_below_working(svn_boolean_t *have_base,
7283 svn_boolean_t *have_work,
7284 svn_wc__db_status_t *status,
7285 svn_wc__db_wcroot_t *wcroot,
7286 const char *local_relpath,
7287 int below_op_depth, /* < 0 is ignored */
7288 apr_pool_t *scratch_pool);
7291 /* Convert STATUS, the raw status obtained from the presence map, to
7292 the status appropriate for a working (op_depth > 0) node and return
7293 it in *WORKING_STATUS. */
7294 static svn_error_t *
7295 convert_to_working_status(svn_wc__db_status_t *working_status,
7296 svn_wc__db_status_t status)
7298 svn_wc__db_status_t work_status = status;
7300 SVN_ERR_ASSERT(work_status == svn_wc__db_status_normal
7301 || work_status == svn_wc__db_status_not_present
7302 || work_status == svn_wc__db_status_base_deleted
7303 || work_status == svn_wc__db_status_incomplete
7304 || work_status == svn_wc__db_status_excluded);
7306 if (work_status == svn_wc__db_status_excluded)
7308 *working_status = svn_wc__db_status_excluded;
7310 else if (work_status == svn_wc__db_status_not_present
7311 || work_status == svn_wc__db_status_base_deleted)
7313 /* The caller should scan upwards to detect whether this
7314 deletion has occurred because this node has been moved
7315 away, or it is a regular deletion. Also note that the
7316 deletion could be of the BASE tree, or a child of
7317 something that has been copied/moved here. */
7319 *working_status = svn_wc__db_status_deleted;
7321 else /* normal or incomplete */
7323 /* The caller should scan upwards to detect whether this
7324 addition has occurred because of a simple addition,
7325 a copy, or is the destination of a move. */
7326 *working_status = svn_wc__db_status_added;
7329 return SVN_NO_ERROR;
7333 /* Return the status of the node, if any, below the "working" node (or
7334 below BELOW_OP_DEPTH if >= 0).
7335 Set *HAVE_BASE or *HAVE_WORK to indicate if a base node or lower
7336 working node is present, and *STATUS to the status of the first
7337 layer below the selected node. */
7338 static svn_error_t *
7339 info_below_working(svn_boolean_t *have_base,
7340 svn_boolean_t *have_work,
7341 svn_wc__db_status_t *status,
7342 svn_wc__db_wcroot_t *wcroot,
7343 const char *local_relpath,
7345 apr_pool_t *scratch_pool)
7347 svn_sqlite__stmt_t *stmt;
7348 svn_boolean_t have_row;
7350 *have_base = *have_work = FALSE;
7351 *status = svn_wc__db_status_normal;
7353 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7354 STMT_SELECT_NODE_INFO));
7355 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7356 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7358 if (below_op_depth >= 0)
7361 (svn_sqlite__column_int(stmt, 0) > below_op_depth))
7363 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7368 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7370 *status = svn_sqlite__column_token(stmt, 3, presence_map);
7374 int op_depth = svn_sqlite__column_int(stmt, 0);
7381 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7384 SVN_ERR(svn_sqlite__reset(stmt));
7387 SVN_ERR(convert_to_working_status(status, *status));
7389 return SVN_NO_ERROR;
7392 /* Helper function for op_delete_txn */
7393 static svn_error_t *
7394 delete_update_movedto(svn_wc__db_wcroot_t *wcroot,
7395 const char *child_moved_from_relpath,
7397 const char *new_moved_to_relpath,
7398 apr_pool_t *scratch_pool)
7400 svn_sqlite__stmt_t *stmt;
7403 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7404 STMT_UPDATE_MOVED_TO_RELPATH));
7406 SVN_ERR(svn_sqlite__bindf(stmt, "isds",
7408 child_moved_from_relpath,
7410 new_moved_to_relpath));
7411 SVN_ERR(svn_sqlite__update(&affected, stmt));
7412 assert(affected == 1);
7414 return SVN_NO_ERROR;
7418 struct op_delete_baton_t {
7419 const char *moved_to_relpath; /* NULL if delete is not part of a move */
7420 svn_skel_t *conflict;
7421 svn_skel_t *work_items;
7422 svn_boolean_t delete_dir_externals;
7423 svn_boolean_t notify;
7426 /* This structure is used while rewriting move information for nodes.
7428 * The most simple case of rewriting move information happens when
7429 * a moved-away subtree is moved again: mv A B; mv B C
7430 * The second move requires rewriting moved-to info at or within A.
7432 * Another example is a move of a subtree which had nodes moved into it:
7434 * This requires rewriting such that A/F is marked has having moved to G/F.
7436 * Another case is where a node becomes a nested moved node.
7437 * A nested move happens when a subtree child is moved before or after
7438 * the subtree itself is moved. For example:
7439 * mv A/F A/G; mv A B
7440 * In this case, the move A/F -> A/G is rewritten to B/F -> B/G.
7441 * Note that the following sequence results in the same DB state:
7442 * mv A B; mv B/F B/G
7443 * We do not care about the order the moves were performed in.
7444 * For details, see http://wiki.apache.org/subversion/MultiLayerMoves
7446 struct moved_node_t {
7447 /* The source of the move. */
7448 const char *local_relpath;
7450 /* The move destination. */
7451 const char *moved_to_relpath;
7453 /* The op-depth of the deleted node at the source of the move. */
7457 static svn_error_t *
7458 delete_node(void *baton,
7459 svn_wc__db_wcroot_t *wcroot,
7460 const char *local_relpath,
7461 apr_pool_t *scratch_pool)
7463 struct op_delete_baton_t *b = baton;
7464 svn_wc__db_status_t status;
7465 svn_boolean_t have_row, op_root;
7466 svn_boolean_t add_work = FALSE;
7467 svn_sqlite__stmt_t *stmt;
7468 int select_depth; /* Depth of what is to be deleted */
7469 svn_boolean_t refetch_depth = FALSE;
7470 svn_node_kind_t kind;
7471 apr_array_header_t *moved_nodes = NULL;
7472 int delete_depth = relpath_depth(local_relpath);
7474 SVN_ERR(read_info(&status,
7475 &kind, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7476 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7477 &op_root, NULL, NULL,
7479 wcroot, local_relpath,
7480 scratch_pool, scratch_pool));
7482 if (status == svn_wc__db_status_deleted
7483 || status == svn_wc__db_status_not_present)
7484 return SVN_NO_ERROR;
7486 /* Don't copy BASE directories with server excluded nodes */
7487 if (status == svn_wc__db_status_normal && kind == svn_node_dir)
7489 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7490 STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
7491 SVN_ERR(svn_sqlite__bindf(stmt, "is",
7492 wcroot->wc_id, local_relpath));
7493 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7496 const char *absent_path = svn_sqlite__column_text(stmt, 0,
7499 return svn_error_createf(
7500 SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
7501 svn_sqlite__reset(stmt),
7502 _("Cannot delete '%s' as '%s' is excluded by server"),
7503 path_for_error_message(wcroot, local_relpath,
7505 path_for_error_message(wcroot, absent_path,
7508 SVN_ERR(svn_sqlite__reset(stmt));
7510 else if (status == svn_wc__db_status_server_excluded)
7512 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
7513 _("Cannot delete '%s' as it is excluded by server"),
7514 path_for_error_message(wcroot, local_relpath,
7517 else if (status == svn_wc__db_status_excluded)
7519 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
7520 _("Cannot delete '%s' as it is excluded"),
7521 path_for_error_message(wcroot, local_relpath,
7525 if (b->moved_to_relpath)
7527 const char *moved_from_relpath = NULL;
7528 struct moved_node_t *moved_node;
7531 moved_nodes = apr_array_make(scratch_pool, 1,
7532 sizeof(struct moved_node_t *));
7534 /* The node is being moved-away.
7535 * Figure out if the node was moved-here before, or whether this
7536 * is the first time the node is moved. */
7537 if (status == svn_wc__db_status_added)
7538 SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL,
7539 &moved_from_relpath,
7542 wcroot, local_relpath,
7543 scratch_pool, scratch_pool));
7545 if (op_root && moved_from_relpath)
7547 const char *part = svn_relpath_skip_ancestor(local_relpath,
7548 moved_from_relpath);
7550 /* Existing move-root is moved to another location */
7551 moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
7553 moved_node->local_relpath = moved_from_relpath;
7555 moved_node->local_relpath = svn_relpath_join(b->moved_to_relpath,
7556 part, scratch_pool);
7557 moved_node->op_depth = move_op_depth;
7558 moved_node->moved_to_relpath = b->moved_to_relpath;
7560 APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
7562 else if (!op_root && (status == svn_wc__db_status_normal
7563 || status == svn_wc__db_status_copied
7564 || status == svn_wc__db_status_moved_here))
7566 /* The node is becoming a move-root for the first time,
7567 * possibly because of a nested move operation. */
7568 moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
7569 moved_node->local_relpath = local_relpath;
7570 moved_node->op_depth = delete_depth;
7571 moved_node->moved_to_relpath = b->moved_to_relpath;
7573 APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
7575 /* Else: We can't track history of local additions and/or of things we are
7578 /* And update all moved_to values still pointing to this location */
7579 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7580 STMT_UPDATE_MOVED_TO_DESCENDANTS));
7581 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
7583 b->moved_to_relpath));
7584 SVN_ERR(svn_sqlite__update(NULL, stmt));
7588 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7589 STMT_CLEAR_MOVED_TO_DESCENDANTS));
7590 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7592 SVN_ERR(svn_sqlite__update(NULL, stmt));
7595 /* Find children that were moved out of the subtree rooted at this node.
7596 * We'll need to update their op-depth columns because their deletion
7597 * is now implied by the deletion of their parent (i.e. this node). */
7599 apr_pool_t *iterpool;
7601 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7602 STMT_SELECT_MOVED_FOR_DELETE));
7603 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7605 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7606 iterpool = svn_pool_create(scratch_pool);
7609 struct moved_node_t *mn;
7610 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7611 const char *mv_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
7612 int child_op_depth = svn_sqlite__column_int(stmt, 2);
7613 svn_boolean_t fixup = FALSE;
7615 if (!b->moved_to_relpath
7616 && ! svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
7618 /* Update the op-depth of an moved node below this tree */
7620 child_op_depth = delete_depth;
7622 else if (b->moved_to_relpath
7623 && delete_depth == child_op_depth)
7625 /* Update the op-depth of a tree shadowed by this tree */
7627 child_op_depth = delete_depth;
7629 else if (b->moved_to_relpath
7630 && child_op_depth >= delete_depth
7631 && !svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
7633 /* Update the move destination of something that is now moved
7636 child_relpath = svn_relpath_skip_ancestor(local_relpath, child_relpath);
7640 child_relpath = svn_relpath_join(b->moved_to_relpath, child_relpath, scratch_pool);
7642 if (child_op_depth > delete_depth
7643 && svn_relpath_skip_ancestor(local_relpath, child_relpath))
7644 child_op_depth = delete_depth;
7646 child_op_depth = relpath_depth(child_relpath);
7654 mn = apr_pcalloc(scratch_pool, sizeof(struct moved_node_t));
7656 mn->local_relpath = apr_pstrdup(scratch_pool, child_relpath);
7657 mn->moved_to_relpath = apr_pstrdup(scratch_pool, mv_to_relpath);
7658 mn->op_depth = child_op_depth;
7661 moved_nodes = apr_array_make(scratch_pool, 1,
7662 sizeof(struct moved_node_t *));
7663 APR_ARRAY_PUSH(moved_nodes, struct moved_node_t *) = mn;
7666 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7668 svn_pool_destroy(iterpool);
7669 SVN_ERR(svn_sqlite__reset(stmt));
7674 svn_boolean_t below_base;
7675 svn_boolean_t below_work;
7676 svn_wc__db_status_t below_status;
7678 /* Use STMT_SELECT_NODE_INFO directly instead of read_info plus
7679 info_below_working */
7680 SVN_ERR(info_below_working(&below_base, &below_work, &below_status,
7681 wcroot, local_relpath, -1, scratch_pool));
7682 if ((below_base || below_work)
7683 && below_status != svn_wc__db_status_not_present
7684 && below_status != svn_wc__db_status_deleted)
7687 refetch_depth = TRUE;
7690 select_depth = relpath_depth(local_relpath);
7695 if (status != svn_wc__db_status_normal)
7696 SVN_ERR(op_depth_of(&select_depth, wcroot, local_relpath));
7698 select_depth = 0; /* Deleting BASE node */
7701 /* ### Put actual-only nodes into the list? */
7704 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7705 STMT_INSERT_DELETE_LIST));
7706 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
7707 wcroot->wc_id, local_relpath, select_depth));
7708 SVN_ERR(svn_sqlite__step_done(stmt));
7711 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7712 STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
7713 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
7714 wcroot->wc_id, local_relpath, delete_depth));
7715 SVN_ERR(svn_sqlite__step_done(stmt));
7718 SVN_ERR(op_depth_of(&select_depth, wcroot, local_relpath));
7720 /* Delete ACTUAL_NODE rows, but leave those that have changelist
7722 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7723 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
7724 SVN_ERR(svn_sqlite__bindf(stmt, "is",
7725 wcroot->wc_id, local_relpath));
7726 SVN_ERR(svn_sqlite__step_done(stmt));
7728 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7729 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
7730 SVN_ERR(svn_sqlite__bindf(stmt, "is",
7731 wcroot->wc_id, local_relpath));
7732 SVN_ERR(svn_sqlite__step_done(stmt));
7734 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7735 STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
7736 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7738 SVN_ERR(svn_sqlite__step_done(stmt));
7742 /* Delete the node at LOCAL_RELPATH, and possibly mark it as moved. */
7744 /* Delete the node and possible descendants. */
7745 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7746 STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
7747 SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
7748 wcroot->wc_id, local_relpath,
7749 select_depth, delete_depth));
7750 SVN_ERR(svn_sqlite__step_done(stmt));
7757 for (i = 0; i < moved_nodes->nelts; ++i)
7759 const struct moved_node_t *moved_node
7760 = APR_ARRAY_IDX(moved_nodes, i, void *);
7762 SVN_ERR(delete_update_movedto(wcroot,
7763 moved_node->local_relpath,
7764 moved_node->op_depth,
7765 moved_node->moved_to_relpath,
7770 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7771 STMT_DELETE_FILE_EXTERNALS));
7772 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7773 SVN_ERR(svn_sqlite__step_done(stmt));
7775 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7776 b->delete_dir_externals
7777 ? STMT_DELETE_EXTERNAL_REGISTATIONS
7778 : STMT_DELETE_FILE_EXTERNAL_REGISTATIONS));
7779 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7780 SVN_ERR(svn_sqlite__step_done(stmt));
7782 SVN_ERR(add_work_items(wcroot->sdb, b->work_items, scratch_pool));
7784 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
7785 b->conflict, scratch_pool));
7787 return SVN_NO_ERROR;
7790 static svn_error_t *
7791 op_delete_txn(void *baton,
7792 svn_wc__db_wcroot_t *wcroot,
7793 const char *local_relpath,
7794 apr_pool_t *scratch_pool)
7797 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
7798 SVN_ERR(delete_node(baton, wcroot, local_relpath, scratch_pool));
7799 return SVN_NO_ERROR;
7803 struct op_delete_many_baton_t {
7804 apr_array_header_t *rel_targets;
7805 svn_boolean_t delete_dir_externals;
7806 const svn_skel_t *work_items;
7807 } op_delete_many_baton_t;
7809 static svn_error_t *
7810 op_delete_many_txn(void *baton,
7811 svn_wc__db_wcroot_t *wcroot,
7812 const char *local_relpath,
7813 apr_pool_t *scratch_pool)
7815 struct op_delete_many_baton_t *odmb = baton;
7816 struct op_delete_baton_t odb;
7818 apr_pool_t *iterpool;
7820 odb.moved_to_relpath = NULL;
7821 odb.conflict = NULL;
7822 odb.work_items = NULL;
7823 odb.delete_dir_externals = odmb->delete_dir_externals;
7826 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
7827 iterpool = svn_pool_create(scratch_pool);
7828 for (i = 0; i < odmb->rel_targets->nelts; i++)
7830 const char *target_relpath = APR_ARRAY_IDX(odmb->rel_targets, i,
7834 svn_pool_clear(iterpool);
7835 SVN_ERR(delete_node(&odb, wcroot, target_relpath, iterpool));
7837 svn_pool_destroy(iterpool);
7839 SVN_ERR(add_work_items(wcroot->sdb, odmb->work_items, scratch_pool));
7841 return SVN_NO_ERROR;
7845 static svn_error_t *
7846 do_delete_notify(void *baton,
7847 svn_wc__db_wcroot_t *wcroot,
7848 svn_cancel_func_t cancel_func,
7850 svn_wc_notify_func2_t notify_func,
7852 apr_pool_t *scratch_pool)
7854 svn_sqlite__stmt_t *stmt;
7855 svn_boolean_t have_row;
7856 apr_pool_t *iterpool;
7858 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7859 STMT_SELECT_DELETE_LIST));
7860 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7862 iterpool = svn_pool_create(scratch_pool);
7865 const char *notify_relpath;
7866 const char *notify_abspath;
7868 svn_pool_clear(iterpool);
7870 notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7871 notify_abspath = svn_dirent_join(wcroot->abspath,
7875 notify_func(notify_baton,
7876 svn_wc_create_notify(notify_abspath,
7877 svn_wc_notify_delete,
7881 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7883 svn_pool_destroy(iterpool);
7885 SVN_ERR(svn_sqlite__reset(stmt));
7887 /* We only allow cancellation after notification for all deleted nodes
7888 * has happened. The nodes are already deleted so we should notify for
7891 SVN_ERR(cancel_func(cancel_baton));
7893 return SVN_NO_ERROR;
7898 svn_wc__db_op_delete(svn_wc__db_t *db,
7899 const char *local_abspath,
7900 const char *moved_to_abspath,
7901 svn_boolean_t delete_dir_externals,
7902 svn_skel_t *conflict,
7903 svn_skel_t *work_items,
7904 svn_cancel_func_t cancel_func,
7906 svn_wc_notify_func2_t notify_func,
7908 apr_pool_t *scratch_pool)
7910 svn_wc__db_wcroot_t *wcroot;
7911 svn_wc__db_wcroot_t *moved_to_wcroot;
7912 const char *local_relpath;
7913 const char *moved_to_relpath;
7914 struct op_delete_baton_t odb;
7916 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7918 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7920 scratch_pool, scratch_pool));
7921 VERIFY_USABLE_WCROOT(wcroot);
7923 if (moved_to_abspath)
7925 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&moved_to_wcroot,
7927 db, moved_to_abspath,
7930 VERIFY_USABLE_WCROOT(moved_to_wcroot);
7932 if (strcmp(wcroot->abspath, moved_to_wcroot->abspath) != 0)
7933 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
7934 _("Cannot move '%s' to '%s' because they "
7935 "are not in the same working copy"),
7936 svn_dirent_local_style(local_abspath,
7938 svn_dirent_local_style(moved_to_abspath,
7942 moved_to_relpath = NULL;
7944 odb.moved_to_relpath = moved_to_relpath;
7945 odb.conflict = conflict;
7946 odb.work_items = work_items;
7947 odb.delete_dir_externals = delete_dir_externals;
7951 /* Perform the deletion operation (transactionally), perform any
7952 notifications necessary, and then clean out our temporary tables. */
7954 SVN_ERR(with_finalization(wcroot, local_relpath,
7955 op_delete_txn, &odb,
7956 do_delete_notify, NULL,
7957 cancel_func, cancel_baton,
7958 notify_func, notify_baton,
7959 STMT_FINALIZE_DELETE,
7964 /* Avoid the trigger work */
7966 SVN_WC__DB_WITH_TXN(
7967 delete_node(&odb, wcroot, local_relpath, scratch_pool),
7971 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
7974 return SVN_NO_ERROR;
7979 svn_wc__db_op_delete_many(svn_wc__db_t *db,
7980 apr_array_header_t *targets,
7981 svn_boolean_t delete_dir_externals,
7982 const svn_skel_t *work_items,
7983 svn_cancel_func_t cancel_func,
7985 svn_wc_notify_func2_t notify_func,
7987 apr_pool_t *scratch_pool)
7989 svn_wc__db_wcroot_t *wcroot;
7990 const char *local_relpath;
7991 struct op_delete_many_baton_t odmb;
7993 apr_pool_t *iterpool;
7995 odmb.rel_targets = apr_array_make(scratch_pool, targets->nelts,
7996 sizeof(const char *));
7997 odmb.work_items = work_items;
7998 odmb.delete_dir_externals = delete_dir_externals;
7999 iterpool = svn_pool_create(scratch_pool);
8000 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8002 APR_ARRAY_IDX(targets, 0,
8004 scratch_pool, iterpool));
8005 VERIFY_USABLE_WCROOT(wcroot);
8006 for (i = 0; i < targets->nelts; i++)
8008 const char *local_abspath = APR_ARRAY_IDX(targets, i, const char*);
8009 svn_wc__db_wcroot_t *target_wcroot;
8011 svn_pool_clear(iterpool);
8013 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&target_wcroot,
8015 APR_ARRAY_IDX(targets, i,
8017 scratch_pool, iterpool));
8018 VERIFY_USABLE_WCROOT(target_wcroot);
8019 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8021 /* Assert that all targets are within the same working copy. */
8022 SVN_ERR_ASSERT(wcroot->wc_id == target_wcroot->wc_id);
8024 APR_ARRAY_PUSH(odmb.rel_targets, const char *) = local_relpath;
8025 SVN_ERR(flush_entries(target_wcroot, local_abspath, svn_depth_infinity,
8029 svn_pool_destroy(iterpool);
8031 /* Perform the deletion operation (transactionally), perform any
8032 notifications necessary, and then clean out our temporary tables. */
8033 return svn_error_trace(with_finalization(wcroot, wcroot->abspath,
8034 op_delete_many_txn, &odmb,
8035 do_delete_notify, NULL,
8036 cancel_func, cancel_baton,
8037 notify_func, notify_baton,
8038 STMT_FINALIZE_DELETE,
8043 /* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of
8044 DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */
8045 static svn_error_t *
8046 read_info(svn_wc__db_status_t *status,
8047 svn_node_kind_t *kind,
8048 svn_revnum_t *revision,
8049 const char **repos_relpath,
8050 apr_int64_t *repos_id,
8051 svn_revnum_t *changed_rev,
8052 apr_time_t *changed_date,
8053 const char **changed_author,
8055 const svn_checksum_t **checksum,
8056 const char **target,
8057 const char **original_repos_relpath,
8058 apr_int64_t *original_repos_id,
8059 svn_revnum_t *original_revision,
8060 svn_wc__db_lock_t **lock,
8061 svn_filesize_t *recorded_size,
8062 apr_time_t *recorded_time,
8063 const char **changelist,
8064 svn_boolean_t *conflicted,
8065 svn_boolean_t *op_root,
8066 svn_boolean_t *had_props,
8067 svn_boolean_t *props_mod,
8068 svn_boolean_t *have_base,
8069 svn_boolean_t *have_more_work,
8070 svn_boolean_t *have_work,
8071 svn_wc__db_wcroot_t *wcroot,
8072 const char *local_relpath,
8073 apr_pool_t *result_pool,
8074 apr_pool_t *scratch_pool)
8076 svn_sqlite__stmt_t *stmt_info;
8077 svn_sqlite__stmt_t *stmt_act;
8078 svn_boolean_t have_info;
8079 svn_boolean_t have_act;
8080 svn_error_t *err = NULL;
8082 /* Obtain the most likely to exist record first, to make sure we don't
8083 have to obtain the SQLite read-lock multiple times */
8084 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
8085 lock ? STMT_SELECT_NODE_INFO_WITH_LOCK
8086 : STMT_SELECT_NODE_INFO));
8087 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
8088 SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
8090 if (changelist || conflicted || props_mod)
8092 SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
8093 STMT_SELECT_ACTUAL_NODE));
8094 SVN_ERR(svn_sqlite__bindf(stmt_act, "is", wcroot->wc_id, local_relpath));
8095 SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
8106 svn_node_kind_t node_kind;
8108 op_depth = svn_sqlite__column_int(stmt_info, 0);
8109 node_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
8113 *status = svn_sqlite__column_token(stmt_info, 3, presence_map);
8115 if (op_depth != 0) /* WORKING */
8116 err = svn_error_compose_create(err,
8117 convert_to_working_status(status,
8127 *repos_id = INVALID_REPOS_ID;
8129 *revision = SVN_INVALID_REVNUM;
8131 /* Our path is implied by our parent somewhere up the tree.
8132 With the NULL value and status, the caller will know to
8133 search up the tree for the base of our path. */
8134 *repos_relpath = NULL;
8138 /* Fetch repository information. If we have a
8139 WORKING_NODE (and have been added), then the repository
8140 we're being added to will be dependent upon a parent. The
8141 caller can scan upwards to locate the repository. */
8142 repos_location_from_columns(repos_id, revision, repos_relpath,
8143 stmt_info, 1, 5, 2, result_pool);
8147 *changed_rev = svn_sqlite__column_revnum(stmt_info, 8);
8151 *changed_date = svn_sqlite__column_int64(stmt_info, 9);
8155 *changed_author = svn_sqlite__column_text(stmt_info, 10,
8160 *recorded_time = svn_sqlite__column_int64(stmt_info, 13);
8164 if (node_kind != svn_node_dir)
8166 *depth = svn_depth_unknown;
8170 *depth = svn_sqlite__column_token_null(stmt_info, 11, depth_map,
8176 if (node_kind != svn_node_file)
8183 err = svn_error_compose_create(
8184 err, svn_sqlite__column_checksum(checksum, stmt_info, 6,
8190 *recorded_size = get_recorded_size(stmt_info, 7);
8194 if (node_kind != svn_node_symlink)
8197 *target = svn_sqlite__column_text(stmt_info, 12, result_pool);
8202 *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8208 if (original_repos_id)
8209 *original_repos_id = INVALID_REPOS_ID;
8210 if (original_revision)
8211 *original_revision = SVN_INVALID_REVNUM;
8212 if (original_repos_relpath)
8213 *original_repos_relpath = NULL;
8217 repos_location_from_columns(original_repos_id,
8219 original_repos_relpath,
8220 stmt_info, 1, 5, 2, result_pool);
8224 *props_mod = have_act && !svn_sqlite__column_is_null(stmt_act, 1);
8228 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt_info, 14);
8235 !svn_sqlite__column_is_null(stmt_act, 2); /* conflict_data */
8238 *conflicted = FALSE;
8246 *lock = lock_from_columns(stmt_info, 17, 18, 19, 20, result_pool);
8250 *have_work = (op_depth != 0);
8254 *op_root = ((op_depth > 0)
8255 && (op_depth == relpath_depth(local_relpath)));
8258 if (have_base || have_more_work)
8261 *have_more_work = FALSE;
8263 while (!err && op_depth != 0)
8265 err = svn_sqlite__step(&have_info, stmt_info);
8267 if (err || !have_info)
8270 op_depth = svn_sqlite__column_int(stmt_info, 0);
8275 *have_more_work = TRUE;
8283 *have_base = (op_depth == 0);
8288 /* A row in ACTUAL_NODE should never exist without a corresponding
8289 node in BASE_NODE and/or WORKING_NODE unless it flags a tree conflict. */
8290 if (svn_sqlite__column_is_null(stmt_act, 2)) /* conflict_data */
8291 err = svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
8292 _("Corrupt data for '%s'"),
8293 path_for_error_message(wcroot, local_relpath,
8295 /* ### What should we return? Should we have a separate
8296 function for reading actual-only nodes? */
8298 /* As a safety measure, until we decide if we want to use
8299 read_info for actual-only nodes, make sure the caller asked
8300 for the conflict status. */
8301 SVN_ERR_ASSERT(conflicted);
8304 *status = svn_wc__db_status_normal; /* What! No it's not! */
8306 *kind = svn_node_unknown;
8308 *revision = SVN_INVALID_REVNUM;
8310 *repos_relpath = NULL;
8312 *repos_id = INVALID_REPOS_ID;
8314 *changed_rev = SVN_INVALID_REVNUM;
8318 *depth = svn_depth_unknown;
8323 if (original_repos_relpath)
8324 *original_repos_relpath = NULL;
8325 if (original_repos_id)
8326 *original_repos_id = INVALID_REPOS_ID;
8327 if (original_revision)
8328 *original_revision = SVN_INVALID_REVNUM;
8336 *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8348 *have_more_work = FALSE;
8354 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
8355 _("The node '%s' was not found."),
8356 path_for_error_message(wcroot, local_relpath,
8360 if (stmt_act != NULL)
8361 err = svn_error_compose_create(err, svn_sqlite__reset(stmt_act));
8363 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
8364 err = svn_error_quick_wrap(err,
8365 apr_psprintf(scratch_pool,
8366 "Error reading node '%s'",
8369 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info)));
8371 return SVN_NO_ERROR;
8376 svn_wc__db_read_info_internal(svn_wc__db_status_t *status,
8377 svn_node_kind_t *kind,
8378 svn_revnum_t *revision,
8379 const char **repos_relpath,
8380 apr_int64_t *repos_id,
8381 svn_revnum_t *changed_rev,
8382 apr_time_t *changed_date,
8383 const char **changed_author,
8385 const svn_checksum_t **checksum,
8386 const char **target,
8387 const char **original_repos_relpath,
8388 apr_int64_t *original_repos_id,
8389 svn_revnum_t *original_revision,
8390 svn_wc__db_lock_t **lock,
8391 svn_filesize_t *recorded_size,
8392 apr_time_t *recorded_time,
8393 const char **changelist,
8394 svn_boolean_t *conflicted,
8395 svn_boolean_t *op_root,
8396 svn_boolean_t *had_props,
8397 svn_boolean_t *props_mod,
8398 svn_boolean_t *have_base,
8399 svn_boolean_t *have_more_work,
8400 svn_boolean_t *have_work,
8401 svn_wc__db_wcroot_t *wcroot,
8402 const char *local_relpath,
8403 apr_pool_t *result_pool,
8404 apr_pool_t *scratch_pool)
8406 return svn_error_trace(
8407 read_info(status, kind, revision, repos_relpath, repos_id,
8408 changed_rev, changed_date, changed_author,
8409 depth, checksum, target, original_repos_relpath,
8410 original_repos_id, original_revision, lock,
8411 recorded_size, recorded_time, changelist, conflicted,
8412 op_root, had_props, props_mod,
8413 have_base, have_more_work, have_work,
8414 wcroot, local_relpath, result_pool, scratch_pool));
8419 svn_wc__db_read_info(svn_wc__db_status_t *status,
8420 svn_node_kind_t *kind,
8421 svn_revnum_t *revision,
8422 const char **repos_relpath,
8423 const char **repos_root_url,
8424 const char **repos_uuid,
8425 svn_revnum_t *changed_rev,
8426 apr_time_t *changed_date,
8427 const char **changed_author,
8429 const svn_checksum_t **checksum,
8430 const char **target,
8431 const char **original_repos_relpath,
8432 const char **original_root_url,
8433 const char **original_uuid,
8434 svn_revnum_t *original_revision,
8435 svn_wc__db_lock_t **lock,
8436 svn_filesize_t *recorded_size,
8437 apr_time_t *recorded_time,
8438 const char **changelist,
8439 svn_boolean_t *conflicted,
8440 svn_boolean_t *op_root,
8441 svn_boolean_t *have_props,
8442 svn_boolean_t *props_mod,
8443 svn_boolean_t *have_base,
8444 svn_boolean_t *have_more_work,
8445 svn_boolean_t *have_work,
8447 const char *local_abspath,
8448 apr_pool_t *result_pool,
8449 apr_pool_t *scratch_pool)
8451 svn_wc__db_wcroot_t *wcroot;
8452 const char *local_relpath;
8453 apr_int64_t repos_id, original_repos_id;
8455 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8457 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
8458 local_abspath, scratch_pool, scratch_pool));
8459 VERIFY_USABLE_WCROOT(wcroot);
8461 SVN_ERR(read_info(status, kind, revision, repos_relpath, &repos_id,
8462 changed_rev, changed_date, changed_author,
8463 depth, checksum, target, original_repos_relpath,
8464 &original_repos_id, original_revision, lock,
8465 recorded_size, recorded_time, changelist, conflicted,
8466 op_root, have_props, props_mod,
8467 have_base, have_more_work, have_work,
8468 wcroot, local_relpath, result_pool, scratch_pool));
8469 SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
8470 wcroot->sdb, repos_id, result_pool));
8471 SVN_ERR(svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
8472 wcroot->sdb, original_repos_id,
8475 return SVN_NO_ERROR;
8478 static svn_error_t *
8479 is_wclocked(svn_boolean_t *locked,
8480 svn_wc__db_wcroot_t *wcroot,
8481 const char *dir_relpath,
8482 apr_pool_t *scratch_pool);
8484 /* What we really want to store about a node. This relies on the
8485 offset of svn_wc__db_info_t being zero. */
8486 struct read_children_info_item_t
8488 struct svn_wc__db_info_t info;
8493 static svn_error_t *
8494 read_children_info(svn_wc__db_wcroot_t *wcroot,
8495 const char *dir_relpath,
8496 apr_hash_t *conflicts,
8498 apr_pool_t *result_pool,
8499 apr_pool_t *scratch_pool)
8501 svn_sqlite__stmt_t *stmt;
8502 svn_boolean_t have_row;
8503 const char *repos_root_url = NULL;
8504 const char *repos_uuid = NULL;
8505 apr_int64_t last_repos_id = INVALID_REPOS_ID;
8507 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8508 STMT_SELECT_NODE_CHILDREN_INFO));
8509 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
8510 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8514 /* CHILD item points to what we have about the node. We only provide
8515 CHILD->item to our caller. */
8516 struct read_children_info_item_t *child_item;
8517 const char *child_relpath = svn_sqlite__column_text(stmt, 19, NULL);
8518 const char *name = svn_relpath_basename(child_relpath, NULL);
8521 svn_boolean_t new_child;
8523 child_item = svn_hash_gets(nodes, name);
8528 child_item = apr_pcalloc(result_pool, sizeof(*child_item));
8532 op_depth = svn_sqlite__column_int(stmt, 0);
8534 /* Do we have new or better information? */
8535 if (new_child || op_depth > child_item->op_depth)
8537 struct svn_wc__db_info_t *child = &child_item->info;
8538 child_item->op_depth = op_depth;
8540 child->kind = svn_sqlite__column_token(stmt, 4, kind_map);
8542 child->status = svn_sqlite__column_token(stmt, 3, presence_map);
8545 if (child->status == svn_wc__db_status_incomplete)
8546 child->incomplete = TRUE;
8547 err = convert_to_working_status(&child->status, child->status);
8549 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8553 child->revnum = SVN_INVALID_REVNUM;
8555 child->revnum = svn_sqlite__column_revnum(stmt, 5);
8558 child->repos_relpath = NULL;
8560 child->repos_relpath = svn_sqlite__column_text(stmt, 2,
8563 if (op_depth != 0 || svn_sqlite__column_is_null(stmt, 1))
8565 child->repos_root_url = NULL;
8566 child->repos_uuid = NULL;
8570 const char *last_repos_root_url = NULL;
8572 apr_int64_t repos_id = svn_sqlite__column_int64(stmt, 1);
8573 if (!repos_root_url ||
8574 (last_repos_id != INVALID_REPOS_ID &&
8575 repos_id != last_repos_id))
8577 last_repos_root_url = repos_root_url;
8578 err = svn_wc__db_fetch_repos_info(&repos_root_url,
8580 wcroot->sdb, repos_id,
8583 SVN_ERR(svn_error_compose_create(err,
8584 svn_sqlite__reset(stmt)));
8587 if (last_repos_id == INVALID_REPOS_ID)
8588 last_repos_id = repos_id;
8590 /* Assume working copy is all one repos_id so that a
8591 single cached value is sufficient. */
8592 if (repos_id != last_repos_id)
8594 err= svn_error_createf(
8595 SVN_ERR_WC_DB_ERROR, NULL,
8596 _("The node '%s' comes from unexpected repository "
8597 "'%s', expected '%s'; if this node is a file "
8598 "external using the correct URL in the external "
8599 "definition can fix the problem, see issue #4087"),
8600 child_relpath, repos_root_url, last_repos_root_url);
8601 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
8603 child->repos_root_url = repos_root_url;
8604 child->repos_uuid = repos_uuid;
8607 child->changed_rev = svn_sqlite__column_revnum(stmt, 8);
8609 child->changed_date = svn_sqlite__column_int64(stmt, 9);
8611 child->changed_author = svn_sqlite__column_text(stmt, 10,
8614 if (child->kind != svn_node_dir)
8615 child->depth = svn_depth_unknown;
8618 child->depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
8621 SVN_ERR(is_wclocked(&child->locked, wcroot, child_relpath,
8625 child->recorded_time = svn_sqlite__column_int64(stmt, 13);
8626 child->recorded_size = get_recorded_size(stmt, 7);
8627 child->has_checksum = !svn_sqlite__column_is_null(stmt, 6);
8628 child->copied = op_depth > 0 && !svn_sqlite__column_is_null(stmt, 2);
8629 child->had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
8631 if (child->had_props)
8633 apr_hash_t *properties;
8634 err = svn_sqlite__column_properties(&properties, stmt, 14,
8635 scratch_pool, scratch_pool);
8637 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8639 child->special = (child->had_props
8640 && svn_hash_gets(properties, SVN_PROP_SPECIAL));
8644 child->op_root = FALSE;
8646 child->op_root = (op_depth == relpath_depth(child_relpath));
8648 svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
8653 child_item->info.have_base = TRUE;
8655 /* Get the lock info, available only at op_depth 0. */
8656 child_item->info.lock = lock_from_columns(stmt, 15, 16, 17, 18,
8659 /* FILE_EXTERNAL flag only on op_depth 0. */
8660 child_item->info.file_external = svn_sqlite__column_boolean(stmt,
8665 const char *moved_to_relpath;
8667 child_item->nr_layers++;
8668 child_item->info.have_more_work = (child_item->nr_layers > 1);
8670 /* Moved-to can only exist at op_depth > 0. */
8671 /* ### Should we really do this for every layer where op_depth > 0
8672 in undefined order? */
8673 moved_to_relpath = svn_sqlite__column_text(stmt, 21, NULL);
8674 if (moved_to_relpath)
8675 child_item->info.moved_to_abspath =
8676 svn_dirent_join(wcroot->abspath, moved_to_relpath, result_pool);
8678 /* Moved-here can only exist at op_depth > 0. */
8679 /* ### Should we really do this for every layer where op_depth > 0
8680 in undefined order? */
8681 child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
8684 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8687 SVN_ERR(svn_sqlite__reset(stmt));
8689 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8690 STMT_SELECT_ACTUAL_CHILDREN_INFO));
8691 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
8692 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8696 struct read_children_info_item_t *child_item;
8697 struct svn_wc__db_info_t *child;
8698 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8699 const char *name = svn_relpath_basename(child_relpath, NULL);
8701 child_item = svn_hash_gets(nodes, name);
8704 child_item = apr_pcalloc(result_pool, sizeof(*child_item));
8705 child_item->info.status = svn_wc__db_status_not_present;
8708 child = &child_item->info;
8710 child->changelist = svn_sqlite__column_text(stmt, 1, result_pool);
8712 child->props_mod = !svn_sqlite__column_is_null(stmt, 2);
8714 if (child->props_mod)
8717 apr_hash_t *properties;
8719 err = svn_sqlite__column_properties(&properties, stmt, 2,
8720 scratch_pool, scratch_pool);
8722 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8723 child->special = (NULL != svn_hash_gets(properties,
8728 child->conflicted = !svn_sqlite__column_is_null(stmt, 3); /* conflict */
8730 if (child->conflicted)
8731 svn_hash_sets(conflicts, apr_pstrdup(result_pool, name), "");
8733 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8736 SVN_ERR(svn_sqlite__reset(stmt));
8738 return SVN_NO_ERROR;
8742 svn_wc__db_read_children_info(apr_hash_t **nodes,
8743 apr_hash_t **conflicts,
8745 const char *dir_abspath,
8746 apr_pool_t *result_pool,
8747 apr_pool_t *scratch_pool)
8749 svn_wc__db_wcroot_t *wcroot;
8750 const char *dir_relpath;
8752 *conflicts = apr_hash_make(result_pool);
8753 *nodes = apr_hash_make(result_pool);
8754 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
8756 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
8758 scratch_pool, scratch_pool));
8759 VERIFY_USABLE_WCROOT(wcroot);
8761 SVN_WC__DB_WITH_TXN(
8762 read_children_info(wcroot, dir_relpath, *conflicts, *nodes,
8763 result_pool, scratch_pool),
8766 return SVN_NO_ERROR;
8770 svn_wc__db_read_pristine_info(svn_wc__db_status_t *status,
8771 svn_node_kind_t *kind,
8772 svn_revnum_t *changed_rev,
8773 apr_time_t *changed_date,
8774 const char **changed_author,
8775 svn_depth_t *depth, /* dirs only */
8776 const svn_checksum_t **checksum, /* files only */
8777 const char **target, /* symlinks only */
8778 svn_boolean_t *had_props,
8781 const char *local_abspath,
8782 apr_pool_t *result_pool,
8783 apr_pool_t *scratch_pool)
8785 svn_wc__db_wcroot_t *wcroot;
8786 const char *local_relpath;
8787 svn_sqlite__stmt_t *stmt;
8788 svn_boolean_t have_row;
8789 svn_error_t *err = NULL;
8791 svn_wc__db_status_t raw_status;
8792 svn_node_kind_t node_kind;
8794 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8796 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
8798 scratch_pool, scratch_pool));
8799 VERIFY_USABLE_WCROOT(wcroot);
8801 /* Obtain the most likely to exist record first, to make sure we don't
8802 have to obtain the SQLite read-lock multiple times */
8803 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8804 STMT_SELECT_NODE_INFO));
8805 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8806 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8810 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
8811 svn_sqlite__reset(stmt),
8812 _("The node '%s' was not found."),
8813 path_for_error_message(wcroot,
8818 op_depth = svn_sqlite__column_int(stmt, 0);
8819 raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
8821 if (op_depth > 0 && raw_status == svn_wc__db_status_base_deleted)
8823 SVN_ERR(svn_sqlite__step_row(stmt));
8825 op_depth = svn_sqlite__column_int(stmt, 0);
8826 raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
8829 node_kind = svn_sqlite__column_token(stmt, 4, kind_map);
8835 err = svn_error_compose_create(err,
8836 convert_to_working_status(
8841 *status = raw_status;
8849 *changed_rev = svn_sqlite__column_revnum(stmt, 8);
8853 *changed_date = svn_sqlite__column_int64(stmt, 9);
8857 *changed_author = svn_sqlite__column_text(stmt, 10,
8862 if (node_kind != svn_node_dir)
8864 *depth = svn_depth_unknown;
8868 *depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
8874 if (node_kind != svn_node_file)
8881 err2 = svn_sqlite__column_checksum(checksum, stmt, 6, result_pool);
8886 err = svn_error_compose_create(
8890 _("The node '%s' has a corrupt checksum value."),
8891 path_for_error_message(wcroot, local_relpath,
8900 if (node_kind != svn_node_symlink)
8903 *target = svn_sqlite__column_text(stmt, 12, result_pool);
8907 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
8911 if (raw_status == svn_wc__db_status_normal
8912 || raw_status == svn_wc__db_status_incomplete)
8914 SVN_ERR(svn_sqlite__column_properties(props, stmt, 14,
8915 result_pool, scratch_pool));
8917 *props = apr_hash_make(result_pool);
8921 assert(svn_sqlite__column_is_null(stmt, 14));
8926 return svn_error_trace(
8927 svn_error_compose_create(err,
8928 svn_sqlite__reset(stmt)));
8932 svn_wc__db_read_children_walker_info(apr_hash_t **nodes,
8934 const char *dir_abspath,
8935 apr_pool_t *result_pool,
8936 apr_pool_t *scratch_pool)
8938 svn_wc__db_wcroot_t *wcroot;
8939 const char *dir_relpath;
8940 svn_sqlite__stmt_t *stmt;
8941 svn_boolean_t have_row;
8943 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
8945 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
8947 scratch_pool, scratch_pool));
8948 VERIFY_USABLE_WCROOT(wcroot);
8950 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8951 STMT_SELECT_NODE_CHILDREN_WALKER_INFO));
8952 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
8953 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8955 *nodes = apr_hash_make(result_pool);
8958 struct svn_wc__db_walker_info_t *child;
8959 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8960 const char *name = svn_relpath_basename(child_relpath, NULL);
8961 int op_depth = svn_sqlite__column_int(stmt, 1);
8964 child = apr_palloc(result_pool, sizeof(*child));
8965 child->status = svn_sqlite__column_token(stmt, 2, presence_map);
8968 err = convert_to_working_status(&child->status, child->status);
8970 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8972 child->kind = svn_sqlite__column_token(stmt, 3, kind_map);
8973 svn_hash_sets(*nodes, apr_pstrdup(result_pool, name), child);
8975 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8978 SVN_ERR(svn_sqlite__reset(stmt));
8980 return SVN_NO_ERROR;
8984 svn_wc__db_read_node_install_info(const char **wcroot_abspath,
8985 const svn_checksum_t **sha1_checksum,
8986 apr_hash_t **pristine_props,
8987 apr_time_t *changed_date,
8989 const char *local_abspath,
8990 const char *wri_abspath,
8991 apr_pool_t *result_pool,
8992 apr_pool_t *scratch_pool)
8994 svn_wc__db_wcroot_t *wcroot;
8995 const char *local_relpath;
8996 svn_sqlite__stmt_t *stmt;
8997 svn_error_t *err = NULL;
8998 svn_boolean_t have_row;
9000 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9003 wri_abspath = local_abspath;
9005 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9006 wri_abspath, scratch_pool, scratch_pool));
9007 VERIFY_USABLE_WCROOT(wcroot);
9009 if (local_abspath != wri_abspath
9010 && strcmp(local_abspath, wri_abspath))
9012 if (!svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
9013 return svn_error_createf(
9014 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
9015 _("The node '%s' is not in working copy '%s'"),
9016 svn_dirent_local_style(local_abspath, scratch_pool),
9017 svn_dirent_local_style(wcroot->abspath, scratch_pool));
9019 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
9022 if (wcroot_abspath != NULL)
9023 *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
9025 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9026 STMT_SELECT_NODE_INFO));
9028 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9030 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9034 if (!err && sha1_checksum)
9035 err = svn_sqlite__column_checksum(sha1_checksum, stmt, 6, result_pool);
9037 if (!err && pristine_props)
9039 err = svn_sqlite__column_properties(pristine_props, stmt, 14,
9040 result_pool, scratch_pool);
9041 /* Null means no props (assuming presence normal or incomplete). */
9042 if (*pristine_props == NULL)
9043 *pristine_props = apr_hash_make(result_pool);
9047 *changed_date = svn_sqlite__column_int64(stmt, 9);
9050 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9051 svn_sqlite__reset(stmt),
9052 _("The node '%s' is not installable"),
9053 svn_dirent_local_style(local_abspath,
9056 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9058 return SVN_NO_ERROR;
9063 /* The body of svn_wc__db_read_url().
9065 static svn_error_t *
9066 read_url_txn(const char **url,
9067 svn_wc__db_wcroot_t *wcroot,
9068 const char *local_relpath,
9069 apr_pool_t *result_pool,
9070 apr_pool_t *scratch_pool)
9072 svn_wc__db_status_t status;
9073 const char *repos_relpath;
9074 const char *repos_root_url;
9075 apr_int64_t repos_id;
9076 svn_boolean_t have_base;
9078 SVN_ERR(read_info(&status, NULL, NULL, &repos_relpath, &repos_id, NULL,
9079 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9080 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9081 &have_base, NULL, NULL,
9082 wcroot, local_relpath, scratch_pool, scratch_pool));
9084 if (repos_relpath == NULL)
9086 if (status == svn_wc__db_status_added)
9088 SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id, NULL,
9089 NULL, NULL, NULL, NULL, NULL,
9090 wcroot, local_relpath,
9091 scratch_pool, scratch_pool));
9093 else if (status == svn_wc__db_status_deleted)
9095 const char *base_del_relpath;
9096 const char *work_del_relpath;
9098 SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
9105 if (base_del_relpath)
9107 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
9112 NULL, NULL, NULL, NULL,
9118 repos_relpath = svn_relpath_join(
9120 svn_dirent_skip_ancestor(base_del_relpath,
9126 /* The parent of the WORKING delete, must be an addition */
9127 const char *work_relpath = NULL;
9129 /* work_del_relpath should not be NULL. However, we have
9130 * observed instances where that assumption was not met.
9131 * Bail out in that case instead of crashing with a segfault.
9133 SVN_ERR_ASSERT(work_del_relpath != NULL);
9134 work_relpath = svn_relpath_dirname(work_del_relpath,
9137 SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id,
9138 NULL, NULL, NULL, NULL, NULL, NULL,
9139 wcroot, work_relpath,
9140 scratch_pool, scratch_pool));
9142 repos_relpath = svn_relpath_join(
9144 svn_dirent_skip_ancestor(work_relpath,
9149 else if (status == svn_wc__db_status_excluded)
9151 const char *parent_relpath;
9155 /* Set 'url' to the *full URL* of the parent WC dir,
9156 * and 'name' to the *single path component* that is the
9157 * basename of this WC directory, so that joining them will result
9158 * in the correct full URL. */
9159 svn_relpath_split(&parent_relpath, &name, local_relpath,
9161 SVN_ERR(read_url_txn(&url2, wcroot, parent_relpath,
9162 scratch_pool, scratch_pool));
9164 *url = svn_path_url_add_component2(url2, name, result_pool);
9166 return SVN_NO_ERROR;
9170 /* All working statee are explicitly handled and all base statee
9171 have a repos_relpath */
9172 SVN_ERR_MALFUNCTION();
9176 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot->sdb,
9177 repos_id, scratch_pool));
9179 SVN_ERR_ASSERT(repos_root_url != NULL && repos_relpath != NULL);
9180 *url = svn_path_url_add_component2(repos_root_url, repos_relpath,
9183 return SVN_NO_ERROR;
9188 svn_wc__db_read_url(const char **url,
9190 const char *local_abspath,
9191 apr_pool_t *result_pool,
9192 apr_pool_t *scratch_pool)
9194 svn_wc__db_wcroot_t *wcroot;
9195 const char *local_relpath;
9197 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9199 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9201 scratch_pool, scratch_pool));
9202 VERIFY_USABLE_WCROOT(wcroot);
9204 SVN_WC__DB_WITH_TXN(read_url_txn(url, wcroot, local_relpath,
9205 result_pool, scratch_pool),
9208 return SVN_NO_ERROR;
9212 /* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
9213 a hash table mapping <tt>char *</tt> names onto svn_string_t *
9214 values for any properties of immediate or recursive child nodes of
9215 LOCAL_ABSPATH, the actual query being determined by STMT_IDX.
9216 If FILES_ONLY is true, only report properties for file child nodes.
9217 Check for cancellation between calls of RECEIVER_FUNC.
9219 typedef struct cache_props_baton_t
9222 svn_boolean_t pristine;
9223 const apr_array_header_t *changelists;
9224 svn_cancel_func_t cancel_func;
9226 } cache_props_baton_t;
9229 static svn_error_t *
9230 cache_props_recursive(void *cb_baton,
9231 svn_wc__db_wcroot_t *wcroot,
9232 const char *local_relpath,
9233 apr_pool_t *scratch_pool)
9235 cache_props_baton_t *baton = cb_baton;
9236 svn_sqlite__stmt_t *stmt;
9239 SVN_ERR(populate_targets_tree(wcroot, local_relpath, baton->depth,
9240 baton->changelists, scratch_pool));
9242 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
9243 STMT_CREATE_TARGET_PROP_CACHE));
9245 if (baton->pristine)
9246 stmt_idx = STMT_CACHE_TARGET_PRISTINE_PROPS;
9248 stmt_idx = STMT_CACHE_TARGET_PROPS;
9250 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
9251 SVN_ERR(svn_sqlite__bind_int64(stmt, 1, wcroot->wc_id));
9252 SVN_ERR(svn_sqlite__step_done(stmt));
9254 return SVN_NO_ERROR;
9259 svn_wc__db_read_props_streamily(svn_wc__db_t *db,
9260 const char *local_abspath,
9262 svn_boolean_t pristine,
9263 const apr_array_header_t *changelists,
9264 svn_wc__proplist_receiver_t receiver_func,
9265 void *receiver_baton,
9266 svn_cancel_func_t cancel_func,
9268 apr_pool_t *scratch_pool)
9270 svn_wc__db_wcroot_t *wcroot;
9271 const char *local_relpath;
9272 svn_sqlite__stmt_t *stmt;
9273 cache_props_baton_t baton;
9274 svn_boolean_t have_row;
9275 apr_pool_t *iterpool;
9276 svn_error_t *err = NULL;
9278 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9279 SVN_ERR_ASSERT(receiver_func);
9280 SVN_ERR_ASSERT((depth == svn_depth_files) ||
9281 (depth == svn_depth_immediates) ||
9282 (depth == svn_depth_infinity));
9284 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
9286 scratch_pool, scratch_pool));
9287 VERIFY_USABLE_WCROOT(wcroot);
9289 baton.depth = depth;
9290 baton.pristine = pristine;
9291 baton.changelists = changelists;
9292 baton.cancel_func = cancel_func;
9293 baton.cancel_baton = cancel_baton;
9295 SVN_ERR(with_finalization(wcroot, local_relpath,
9296 cache_props_recursive, &baton,
9298 cancel_func, cancel_baton,
9300 STMT_DROP_TARGETS_LIST,
9303 iterpool = svn_pool_create(scratch_pool);
9305 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9306 STMT_SELECT_ALL_TARGET_PROP_CACHE));
9307 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9308 while (!err && have_row)
9312 svn_pool_clear(iterpool);
9314 SVN_ERR(svn_sqlite__column_properties(&props, stmt, 1, iterpool,
9317 /* see if someone wants to cancel this operation. */
9319 err = cancel_func(cancel_baton);
9321 if (!err && props && apr_hash_count(props) != 0)
9323 const char *child_relpath;
9324 const char *child_abspath;
9326 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9327 child_abspath = svn_dirent_join(wcroot->abspath,
9328 child_relpath, iterpool);
9330 err = receiver_func(receiver_baton, child_abspath, props, iterpool);
9333 err = svn_error_compose_create(err, svn_sqlite__step(&have_row, stmt));
9336 err = svn_error_compose_create(err, svn_sqlite__reset(stmt));
9338 svn_pool_destroy(iterpool);
9340 SVN_ERR(svn_error_compose_create(
9342 svn_sqlite__exec_statements(wcroot->sdb,
9343 STMT_DROP_TARGET_PROP_CACHE)));
9344 return SVN_NO_ERROR;
9348 /* Helper for svn_wc__db_read_props().
9350 static svn_error_t *
9351 db_read_props(apr_hash_t **props,
9352 svn_wc__db_wcroot_t *wcroot,
9353 const char *local_relpath,
9354 apr_pool_t *result_pool,
9355 apr_pool_t *scratch_pool)
9357 svn_sqlite__stmt_t *stmt;
9358 svn_boolean_t have_row;
9359 svn_error_t *err = NULL;
9361 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9362 STMT_SELECT_ACTUAL_PROPS));
9363 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9364 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9366 if (have_row && !svn_sqlite__column_is_null(stmt, 0))
9368 err = svn_sqlite__column_properties(props, stmt, 0,
9369 result_pool, scratch_pool);
9374 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9377 return SVN_NO_ERROR;
9379 /* No local changes. Return the pristine props for this node. */
9380 SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, FALSE,
9381 result_pool, scratch_pool));
9384 /* Pristine properties are not defined for this node.
9385 ### we need to determine whether this node is in a state that
9386 ### allows for ACTUAL properties (ie. not deleted). for now,
9387 ### just say all nodes, no matter the state, have at least an
9388 ### empty set of props. */
9389 *props = apr_hash_make(result_pool);
9392 return SVN_NO_ERROR;
9397 svn_wc__db_read_props(apr_hash_t **props,
9399 const char *local_abspath,
9400 apr_pool_t *result_pool,
9401 apr_pool_t *scratch_pool)
9403 svn_wc__db_wcroot_t *wcroot;
9404 const char *local_relpath;
9406 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9408 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9409 local_abspath, scratch_pool, scratch_pool));
9410 VERIFY_USABLE_WCROOT(wcroot);
9412 SVN_WC__DB_WITH_TXN(db_read_props(props, wcroot, local_relpath,
9413 result_pool, scratch_pool),
9416 return SVN_NO_ERROR;
9420 static svn_error_t *
9421 db_read_pristine_props(apr_hash_t **props,
9422 svn_wc__db_wcroot_t *wcroot,
9423 const char *local_relpath,
9424 svn_boolean_t deleted_ok,
9425 apr_pool_t *result_pool,
9426 apr_pool_t *scratch_pool)
9428 svn_sqlite__stmt_t *stmt;
9429 svn_boolean_t have_row;
9430 svn_wc__db_status_t presence;
9432 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_NODE_PROPS));
9433 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9435 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9439 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9440 svn_sqlite__reset(stmt),
9441 _("The node '%s' was not found."),
9442 path_for_error_message(wcroot,
9448 /* Examine the presence: */
9449 presence = svn_sqlite__column_token(stmt, 1, presence_map);
9451 /* For "base-deleted", it is obvious the pristine props are located
9452 below the current node. Fetch the NODE from the next record. */
9453 if (presence == svn_wc__db_status_base_deleted && deleted_ok)
9455 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9457 SVN_ERR_ASSERT(have_row);
9459 presence = svn_sqlite__column_token(stmt, 1, presence_map);
9462 /* normal or copied: Fetch properties (during update we want
9463 properties for incomplete as well) */
9464 if (presence == svn_wc__db_status_normal
9465 || presence == svn_wc__db_status_incomplete)
9469 err = svn_sqlite__column_properties(props, stmt, 0, result_pool,
9471 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9474 *props = apr_hash_make(result_pool);
9476 return SVN_NO_ERROR;
9479 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
9480 svn_sqlite__reset(stmt),
9481 _("The node '%s' has a status that"
9482 " has no properties."),
9483 path_for_error_message(wcroot,
9490 svn_wc__db_read_pristine_props(apr_hash_t **props,
9492 const char *local_abspath,
9493 apr_pool_t *result_pool,
9494 apr_pool_t *scratch_pool)
9496 svn_wc__db_wcroot_t *wcroot;
9497 const char *local_relpath;
9499 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9501 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9502 local_abspath, scratch_pool, scratch_pool));
9503 VERIFY_USABLE_WCROOT(wcroot);
9505 SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, TRUE,
9506 result_pool, scratch_pool));
9507 return SVN_NO_ERROR;
9511 svn_wc__db_prop_retrieve_recursive(apr_hash_t **values,
9513 const char *local_abspath,
9514 const char *propname,
9515 apr_pool_t *result_pool,
9516 apr_pool_t *scratch_pool)
9518 svn_wc__db_wcroot_t *wcroot;
9519 const char *local_relpath;
9520 svn_sqlite__stmt_t *stmt;
9521 svn_boolean_t have_row;
9522 apr_pool_t *iterpool;
9524 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9526 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9527 local_abspath, scratch_pool, scratch_pool));
9528 VERIFY_USABLE_WCROOT(wcroot);
9530 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9531 STMT_SELECT_CURRENT_PROPS_RECURSIVE));
9532 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9534 *values = apr_hash_make(result_pool);
9536 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9537 iterpool = svn_pool_create(scratch_pool);
9540 apr_hash_t *node_props;
9541 svn_string_t *value;
9543 svn_pool_clear(iterpool);
9545 SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 0,
9546 iterpool, iterpool));
9549 ? svn_hash_gets(node_props, propname)
9554 svn_hash_sets(*values,
9555 svn_dirent_join(wcroot->abspath,
9556 svn_sqlite__column_text(stmt, 1, NULL),
9558 svn_string_dup(value, result_pool));
9561 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9564 svn_pool_destroy(iterpool);
9566 return svn_error_trace(svn_sqlite__reset(stmt));
9569 /* The body of svn_wc__db_read_cached_iprops(). */
9570 static svn_error_t *
9571 db_read_cached_iprops(apr_array_header_t **iprops,
9572 svn_wc__db_wcroot_t *wcroot,
9573 const char *local_relpath,
9574 apr_pool_t *result_pool,
9575 apr_pool_t *scratch_pool)
9577 svn_sqlite__stmt_t *stmt;
9578 svn_boolean_t have_row;
9580 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_IPROPS));
9581 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9582 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9586 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9587 svn_sqlite__reset(stmt),
9588 _("The node '%s' was not found."),
9589 path_for_error_message(wcroot, local_relpath,
9593 SVN_ERR(svn_sqlite__column_iprops(iprops, stmt, 0,
9594 result_pool, scratch_pool));
9596 SVN_ERR(svn_sqlite__reset(stmt));
9598 return SVN_NO_ERROR;
9602 svn_wc__db_read_cached_iprops(apr_array_header_t **iprops,
9604 const char *local_abspath,
9605 apr_pool_t *result_pool,
9606 apr_pool_t *scratch_pool)
9608 svn_wc__db_wcroot_t *wcroot;
9609 const char *local_relpath;
9611 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9613 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
9615 scratch_pool, scratch_pool));
9616 VERIFY_USABLE_WCROOT(wcroot);
9618 /* Don't use with_txn yet, as we perform just a single transaction */
9619 SVN_ERR(db_read_cached_iprops(iprops, wcroot, local_relpath,
9620 result_pool, scratch_pool));
9624 *iprops = apr_array_make(result_pool, 0,
9625 sizeof(svn_prop_inherited_item_t *));
9628 return SVN_NO_ERROR;
9631 /* Remove all prop name value pairs from PROP_HASH where the property
9632 name is not PROPNAME. */
9634 filter_unwanted_props(apr_hash_t *prop_hash,
9635 const char * propname,
9636 apr_pool_t *scratch_pool)
9638 apr_hash_index_t *hi;
9640 for (hi = apr_hash_first(scratch_pool, prop_hash);
9642 hi = apr_hash_next(hi))
9644 const char *ipropname = svn__apr_hash_index_key(hi);
9646 if (strcmp(ipropname, propname) != 0)
9647 svn_hash_sets(prop_hash, ipropname, NULL);
9652 /* Get the changed properties as stored in the ACTUAL table */
9653 static svn_error_t *
9654 db_get_changed_props(apr_hash_t **actual_props,
9655 svn_wc__db_wcroot_t *wcroot,
9656 const char *local_relpath,
9657 apr_pool_t *result_pool,
9658 apr_pool_t *scratch_pool)
9660 svn_sqlite__stmt_t *stmt;
9661 svn_boolean_t have_row;
9662 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9663 STMT_SELECT_ACTUAL_PROPS));
9664 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9665 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9667 if (have_row && !svn_sqlite__column_is_null(stmt, 0))
9668 SVN_ERR(svn_sqlite__column_properties(actual_props, stmt, 0,
9669 result_pool, scratch_pool));
9671 *actual_props = NULL; /* Cached when we read that record */
9673 return svn_error_trace(svn_sqlite__reset(stmt));
9676 /* The body of svn_wc__db_read_inherited_props(). */
9677 static svn_error_t *
9678 db_read_inherited_props(apr_array_header_t **inherited_props,
9679 apr_hash_t **actual_props,
9680 svn_wc__db_wcroot_t *wcroot,
9681 const char *local_relpath,
9682 const char *propname,
9683 apr_pool_t *result_pool,
9684 apr_pool_t *scratch_pool)
9687 apr_array_header_t *cached_iprops = NULL;
9688 apr_array_header_t *iprops;
9689 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
9690 svn_sqlite__stmt_t *stmt;
9691 const char *relpath;
9692 const char *expected_parent_repos_relpath = NULL;
9693 const char *parent_relpath;
9695 iprops = apr_array_make(result_pool, 1,
9696 sizeof(svn_prop_inherited_item_t *));
9697 *inherited_props = iprops;
9700 *actual_props = NULL;
9702 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9703 STMT_SELECT_NODE_INFO));
9705 relpath = local_relpath;
9707 /* Walk up to the root of the WC looking for inherited properties. When we
9708 reach the WC root also check for cached inherited properties. */
9709 for (relpath = local_relpath; relpath; relpath = parent_relpath)
9711 svn_boolean_t have_row;
9713 svn_wc__db_status_t status;
9714 apr_hash_t *node_props;
9716 parent_relpath = relpath[0] ? svn_relpath_dirname(relpath, scratch_pool)
9719 svn_pool_clear(iterpool);
9721 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, relpath));
9723 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9726 return svn_error_createf(
9727 SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
9728 _("The node '%s' was not found."),
9729 path_for_error_message(wcroot, relpath,
9732 op_depth = svn_sqlite__column_int(stmt, 0);
9734 status = svn_sqlite__column_token(stmt, 3, presence_map);
9736 if (status != svn_wc__db_status_normal
9737 && status != svn_wc__db_status_incomplete)
9738 return svn_error_createf(
9739 SVN_ERR_WC_PATH_UNEXPECTED_STATUS, svn_sqlite__reset(stmt),
9740 _("The node '%s' has a status that has no properties."),
9741 path_for_error_message(wcroot, relpath,
9746 /* WORKING node. Nothing to check */
9748 else if (expected_parent_repos_relpath)
9750 const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
9752 if (strcmp(expected_parent_repos_relpath, repos_relpath) != 0)
9754 /* The child of this node has a different parent than this node
9755 (It is "switched"), so we can stop here. Note that switched
9756 with the same parent is not interesting for us here. */
9757 SVN_ERR(svn_sqlite__reset(stmt));
9761 expected_parent_repos_relpath =
9762 svn_relpath_dirname(expected_parent_repos_relpath, scratch_pool);
9766 const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
9768 expected_parent_repos_relpath =
9769 svn_relpath_dirname(repos_relpath, scratch_pool);
9773 && !svn_sqlite__column_is_null(stmt, 16))
9775 /* The node contains a cache. No reason to look further */
9776 SVN_ERR(svn_sqlite__column_iprops(&cached_iprops, stmt, 16,
9777 result_pool, iterpool));
9779 parent_relpath = NULL; /* Stop after this */
9782 SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 14,
9783 iterpool, iterpool));
9785 SVN_ERR(svn_sqlite__reset(stmt));
9787 /* If PARENT_ABSPATH is a parent of LOCAL_ABSPATH, then LOCAL_ABSPATH
9788 can inherit properties from it. */
9789 if (relpath != local_relpath)
9791 apr_hash_t *changed_props;
9793 SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
9794 result_pool, iterpool));
9797 node_props = changed_props;
9798 else if (node_props)
9799 node_props = svn_prop_hash_dup(node_props, result_pool);
9801 if (node_props && apr_hash_count(node_props))
9803 /* If we only want PROPNAME filter out any other properties. */
9805 filter_unwanted_props(node_props, propname, iterpool);
9807 if (apr_hash_count(node_props))
9809 svn_prop_inherited_item_t *iprop_elt =
9810 apr_pcalloc(result_pool,
9811 sizeof(svn_prop_inherited_item_t));
9812 iprop_elt->path_or_url = svn_dirent_join(wcroot->abspath,
9816 iprop_elt->prop_hash = node_props;
9817 /* Build the output array in depth-first order. */
9818 svn_sort__array_insert(&iprop_elt, iprops, 0);
9822 else if (actual_props)
9824 apr_hash_t *changed_props;
9826 SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
9827 result_pool, iterpool));
9830 *actual_props = changed_props;
9831 else if (node_props)
9832 *actual_props = svn_prop_hash_dup(node_props, result_pool);
9838 for (i = cached_iprops->nelts - 1; i >= 0; i--)
9840 svn_prop_inherited_item_t *cached_iprop =
9841 APR_ARRAY_IDX(cached_iprops, i, svn_prop_inherited_item_t *);
9843 /* An empty property hash in the iprops cache means there are no
9844 inherited properties. */
9845 if (apr_hash_count(cached_iprop->prop_hash) == 0)
9849 filter_unwanted_props(cached_iprop->prop_hash, propname,
9852 /* If we didn't filter everything then keep this iprop. */
9853 if (apr_hash_count(cached_iprop->prop_hash))
9854 svn_sort__array_insert(&cached_iprop, iprops, 0);
9858 if (actual_props && !*actual_props)
9859 *actual_props = apr_hash_make(result_pool);
9861 svn_pool_destroy(iterpool);
9862 return SVN_NO_ERROR;
9866 svn_wc__db_read_inherited_props(apr_array_header_t **iprops,
9867 apr_hash_t **actual_props,
9869 const char *local_abspath,
9870 const char *propname,
9871 apr_pool_t *result_pool,
9872 apr_pool_t *scratch_pool)
9874 svn_wc__db_wcroot_t *wcroot;
9875 const char *local_relpath;
9877 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9879 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
9881 scratch_pool, scratch_pool));
9882 VERIFY_USABLE_WCROOT(wcroot);
9884 SVN_WC__DB_WITH_TXN(db_read_inherited_props(iprops, actual_props,
9885 wcroot, local_relpath, propname,
9886 result_pool, scratch_pool),
9889 return SVN_NO_ERROR;
9892 /* The body of svn_wc__db_get_children_with_cached_iprops().
9894 static svn_error_t *
9895 get_children_with_cached_iprops(apr_hash_t **iprop_paths,
9896 svn_wc__db_wcroot_t *wcroot,
9897 const char *local_relpath,
9899 apr_pool_t *result_pool,
9900 apr_pool_t *scratch_pool)
9902 svn_sqlite__stmt_t *stmt;
9903 svn_boolean_t have_row;
9905 *iprop_paths = apr_hash_make(result_pool);
9907 /* First check if LOCAL_RELPATH itself has iprops */
9908 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9909 STMT_SELECT_IPROPS_NODE));
9910 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9911 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9915 const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
9917 const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
9920 svn_hash_sets(*iprop_paths, abspath_with_cache,
9921 svn_sqlite__column_text(stmt, 1, result_pool));
9923 SVN_ERR(svn_sqlite__reset(stmt));
9925 if (depth == svn_depth_empty)
9926 return SVN_NO_ERROR;
9928 /* Now fetch information for children or all descendants */
9929 if (depth == svn_depth_files
9930 || depth == svn_depth_immediates)
9932 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9933 STMT_SELECT_IPROPS_CHILDREN));
9935 else /* Default to svn_depth_infinity. */
9937 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9938 STMT_SELECT_IPROPS_RECURSIVE));
9941 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9942 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9946 const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
9948 const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
9951 svn_hash_sets(*iprop_paths, abspath_with_cache,
9952 svn_sqlite__column_text(stmt, 1, result_pool));
9953 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9956 SVN_ERR(svn_sqlite__reset(stmt));
9958 /* For depth files we should filter non files */
9959 if (depth == svn_depth_files)
9961 apr_hash_index_t *hi;
9962 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
9964 for (hi = apr_hash_first(scratch_pool, *iprop_paths);
9966 hi = apr_hash_next(hi))
9968 const char *child_abspath = svn__apr_hash_index_key(hi);
9969 const char *child_relpath;
9970 svn_node_kind_t child_kind;
9972 svn_pool_clear(iterpool);
9974 child_relpath = svn_dirent_is_child(local_relpath, child_abspath,
9977 if (! child_relpath)
9979 continue; /* local_relpath itself */
9982 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &child_kind, NULL,
9983 NULL, NULL, NULL, NULL,
9984 NULL, NULL, NULL, NULL,
9985 NULL, NULL, NULL, NULL,
9986 wcroot, child_relpath,
9990 /* Filter if not a file */
9991 if (child_kind != svn_node_file)
9993 svn_hash_sets(*iprop_paths, child_abspath, NULL);
9997 svn_pool_destroy(iterpool);
10000 return SVN_NO_ERROR;
10004 svn_wc__db_get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10006 const char *local_abspath,
10008 apr_pool_t *result_pool,
10009 apr_pool_t *scratch_pool)
10011 svn_wc__db_wcroot_t *wcroot;
10012 const char *local_relpath;
10014 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10016 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10017 local_abspath, scratch_pool,
10019 VERIFY_USABLE_WCROOT(wcroot);
10021 SVN_WC__DB_WITH_TXN(
10022 get_children_with_cached_iprops(iprop_paths, wcroot, local_relpath,
10023 depth, result_pool, scratch_pool),
10026 return SVN_NO_ERROR;
10030 svn_wc__db_read_children_of_working_node(const apr_array_header_t **children,
10032 const char *local_abspath,
10033 apr_pool_t *result_pool,
10034 apr_pool_t *scratch_pool)
10036 svn_wc__db_wcroot_t *wcroot;
10037 const char *local_relpath;
10039 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10041 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10043 scratch_pool, scratch_pool));
10044 VERIFY_USABLE_WCROOT(wcroot);
10046 return gather_children2(children, wcroot, local_relpath,
10047 result_pool, scratch_pool);
10050 /* Helper for svn_wc__db_node_check_replace().
10052 static svn_error_t *
10053 check_replace_txn(svn_boolean_t *is_replace_root_p,
10054 svn_boolean_t *base_replace_p,
10055 svn_boolean_t *is_replace_p,
10056 svn_wc__db_wcroot_t *wcroot,
10057 const char *local_relpath,
10058 apr_pool_t *scratch_pool)
10060 svn_sqlite__stmt_t *stmt;
10061 svn_boolean_t have_row;
10062 svn_boolean_t is_replace = FALSE;
10063 int replaced_op_depth;
10064 svn_wc__db_status_t replaced_status;
10066 /* Our caller initialized the output values to FALSE */
10068 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10069 STMT_SELECT_NODE_INFO));
10071 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10073 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10076 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10077 svn_sqlite__reset(stmt),
10078 _("The node '%s' was not found."),
10079 path_for_error_message(wcroot, local_relpath,
10083 svn_wc__db_status_t status;
10085 status = svn_sqlite__column_token(stmt, 3, presence_map);
10087 if (status != svn_wc__db_status_normal)
10088 return svn_error_trace(svn_sqlite__reset(stmt));
10091 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10094 return svn_error_trace(svn_sqlite__reset(stmt));
10096 replaced_status = svn_sqlite__column_token(stmt, 3, presence_map);
10098 /* If the layer below the add describes a not present or a deleted node,
10099 this is not a replacement. Deleted can only occur if an ancestor is
10100 the delete root. */
10101 if (replaced_status != svn_wc__db_status_not_present
10102 && replaced_status != svn_wc__db_status_excluded
10103 && replaced_status != svn_wc__db_status_server_excluded
10104 && replaced_status != svn_wc__db_status_base_deleted)
10108 *is_replace_p = TRUE;
10111 replaced_op_depth = svn_sqlite__column_int(stmt, 0);
10113 if (base_replace_p)
10115 int op_depth = svn_sqlite__column_int(stmt, 0);
10117 while (op_depth != 0 && have_row)
10119 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10122 op_depth = svn_sqlite__column_int(stmt, 0);
10125 if (have_row && op_depth == 0)
10127 svn_wc__db_status_t base_status;
10129 base_status = svn_sqlite__column_token(stmt, 3, presence_map);
10131 *base_replace_p = (base_status != svn_wc__db_status_not_present);
10135 SVN_ERR(svn_sqlite__reset(stmt));
10137 if (!is_replace_root_p || !is_replace)
10138 return SVN_NO_ERROR;
10140 if (replaced_status != svn_wc__db_status_base_deleted)
10142 int parent_op_depth;
10144 /* Check the current op-depth of the parent to see if we are a replacement
10146 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
10147 svn_relpath_dirname(local_relpath,
10150 SVN_ERR(svn_sqlite__step_row(stmt)); /* Parent must exist as 'normal' */
10152 parent_op_depth = svn_sqlite__column_int(stmt, 0);
10154 if (parent_op_depth >= replaced_op_depth)
10156 /* Did we replace inside our directory? */
10158 *is_replace_root_p = (parent_op_depth == replaced_op_depth);
10159 SVN_ERR(svn_sqlite__reset(stmt));
10160 return SVN_NO_ERROR;
10163 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10166 parent_op_depth = svn_sqlite__column_int(stmt, 0);
10168 SVN_ERR(svn_sqlite__reset(stmt));
10171 *is_replace_root_p = TRUE; /* Parent is no replacement */
10172 else if (parent_op_depth < replaced_op_depth)
10173 *is_replace_root_p = TRUE; /* Parent replaces a lower layer */
10174 /*else // No replacement root */
10177 return SVN_NO_ERROR;
10181 svn_wc__db_node_check_replace(svn_boolean_t *is_replace_root,
10182 svn_boolean_t *base_replace,
10183 svn_boolean_t *is_replace,
10185 const char *local_abspath,
10186 apr_pool_t *scratch_pool)
10188 svn_wc__db_wcroot_t *wcroot;
10189 const char *local_relpath;
10191 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10193 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10195 scratch_pool, scratch_pool));
10196 VERIFY_USABLE_WCROOT(wcroot);
10198 if (is_replace_root)
10199 *is_replace_root = FALSE;
10201 *base_replace = FALSE;
10203 *is_replace = FALSE;
10205 if (local_relpath[0] == '\0')
10206 return SVN_NO_ERROR; /* Working copy root can't be replaced */
10208 SVN_WC__DB_WITH_TXN(
10209 check_replace_txn(is_replace_root, base_replace, is_replace,
10210 wcroot, local_relpath, scratch_pool),
10213 return SVN_NO_ERROR;
10217 svn_wc__db_read_children(const apr_array_header_t **children,
10219 const char *local_abspath,
10220 apr_pool_t *result_pool,
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 return gather_children(children, wcroot, local_relpath,
10234 result_pool, scratch_pool);
10239 static svn_error_t *
10240 relocate_txn(svn_wc__db_wcroot_t *wcroot,
10241 const char *local_relpath,
10242 const char *repos_root_url,
10243 const char *repos_uuid,
10244 svn_boolean_t have_base_node,
10245 apr_int64_t old_repos_id,
10246 apr_pool_t *scratch_pool)
10248 svn_sqlite__stmt_t *stmt;
10249 apr_int64_t new_repos_id;
10251 /* This function affects all the children of the given local_relpath,
10252 but the way that it does this is through the repos inheritance mechanism.
10253 So, we only need to rewrite the repos_id of the given local_relpath,
10254 as well as any children with a non-null repos_id, as well as various
10255 repos_id fields in the locks and working_node tables.
10258 /* Get the repos_id for the new repository. */
10259 SVN_ERR(create_repos_id(&new_repos_id, repos_root_url, repos_uuid,
10260 wcroot->sdb, scratch_pool));
10262 /* Set the (base and working) repos_ids and clear the dav_caches */
10263 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10264 STMT_RECURSIVE_UPDATE_NODE_REPO));
10265 SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
10266 old_repos_id, new_repos_id));
10267 SVN_ERR(svn_sqlite__step_done(stmt));
10269 if (have_base_node)
10271 /* Update any locks for the root or its children. */
10272 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10273 STMT_UPDATE_LOCK_REPOS_ID));
10274 SVN_ERR(svn_sqlite__bindf(stmt, "ii", old_repos_id, new_repos_id));
10275 SVN_ERR(svn_sqlite__step_done(stmt));
10278 return SVN_NO_ERROR;
10283 svn_wc__db_global_relocate(svn_wc__db_t *db,
10284 const char *local_dir_abspath,
10285 const char *repos_root_url,
10286 apr_pool_t *scratch_pool)
10288 svn_wc__db_wcroot_t *wcroot;
10289 const char *local_relpath;
10290 const char *local_dir_relpath;
10291 svn_wc__db_status_t status;
10292 const char *repos_uuid;
10293 svn_boolean_t have_base_node;
10294 apr_int64_t old_repos_id;
10296 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
10297 /* ### assert that we were passed a directory? */
10299 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_dir_relpath,
10300 db, local_dir_abspath, scratch_pool, scratch_pool));
10301 VERIFY_USABLE_WCROOT(wcroot);
10302 local_relpath = local_dir_relpath;
10304 SVN_ERR(read_info(&status,
10305 NULL, NULL, NULL, &old_repos_id,
10306 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10307 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10309 &have_base_node, NULL, NULL,
10310 wcroot, local_relpath,
10311 scratch_pool, scratch_pool));
10313 if (status == svn_wc__db_status_excluded)
10315 /* The parent cannot be excluded, so look at the parent and then
10316 adjust the relpath */
10317 const char *parent_relpath = svn_relpath_dirname(local_dir_relpath,
10319 SVN_ERR(read_info(&status,
10320 NULL, NULL, NULL, &old_repos_id,
10321 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10322 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10325 wcroot, parent_relpath,
10326 scratch_pool, scratch_pool));
10327 local_dir_relpath = parent_relpath;
10330 if (old_repos_id == INVALID_REPOS_ID)
10332 /* Do we need to support relocating something that is
10333 added/deleted/excluded without relocating the parent? If not
10334 then perhaps relpath, root_url and uuid should be passed down
10335 to the children so that they don't have to scan? */
10337 if (status == svn_wc__db_status_deleted)
10339 const char *work_del_relpath;
10341 SVN_ERR(scan_deletion_txn(NULL, NULL,
10342 &work_del_relpath, NULL,
10343 wcroot, local_dir_relpath,
10346 if (work_del_relpath)
10348 /* Deleted within a copy/move */
10350 /* The parent of the delete is added. */
10351 status = svn_wc__db_status_added;
10352 local_dir_relpath = svn_relpath_dirname(work_del_relpath,
10357 if (status == svn_wc__db_status_added)
10359 SVN_ERR(scan_addition(NULL, NULL, NULL, &old_repos_id,
10360 NULL, NULL, NULL, NULL, NULL, NULL,
10361 wcroot, local_dir_relpath,
10362 scratch_pool, scratch_pool));
10365 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL,
10367 NULL, NULL, NULL, NULL, NULL,
10368 NULL, NULL, NULL, NULL, NULL,
10369 wcroot, local_dir_relpath,
10370 scratch_pool, scratch_pool));
10373 SVN_ERR(svn_wc__db_fetch_repos_info(NULL, &repos_uuid, wcroot->sdb,
10374 old_repos_id, scratch_pool));
10375 SVN_ERR_ASSERT(repos_uuid);
10377 SVN_WC__DB_WITH_TXN(
10378 relocate_txn(wcroot, local_relpath, repos_root_url, repos_uuid,
10379 have_base_node, old_repos_id, scratch_pool),
10382 return SVN_NO_ERROR;
10386 /* Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of
10387 (WCROOT, LOCAL_RELPATH), directly if its BASE row exists or implied from
10388 its parent's BASE row if not. In the latter case, error if the parent
10389 BASE row does not exist. */
10390 static svn_error_t *
10391 determine_repos_info(apr_int64_t *repos_id,
10392 const char **repos_relpath,
10393 svn_wc__db_wcroot_t *wcroot,
10394 const char *local_relpath,
10395 apr_pool_t *result_pool,
10396 apr_pool_t *scratch_pool)
10398 svn_sqlite__stmt_t *stmt;
10399 svn_boolean_t have_row;
10400 const char *repos_parent_relpath;
10401 const char *local_parent_relpath, *name;
10403 /* ### is it faster to fetch fewer columns? */
10405 /* Prefer the current node's repository information. */
10406 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10407 STMT_SELECT_BASE_NODE));
10408 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10409 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10413 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 0));
10414 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1));
10416 *repos_id = svn_sqlite__column_int64(stmt, 0);
10417 *repos_relpath = svn_sqlite__column_text(stmt, 1, result_pool);
10419 return svn_error_trace(svn_sqlite__reset(stmt));
10422 SVN_ERR(svn_sqlite__reset(stmt));
10424 /* This was a child node within this wcroot. We want to look at the
10425 BASE node of the directory. */
10426 svn_relpath_split(&local_parent_relpath, &name, local_relpath, scratch_pool);
10428 /* The REPOS_ID will be the same (### until we support mixed-repos) */
10429 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
10430 &repos_parent_relpath, repos_id,
10431 NULL, NULL, NULL, NULL, NULL,
10432 NULL, NULL, NULL, NULL, NULL,
10433 wcroot, local_parent_relpath,
10434 scratch_pool, scratch_pool));
10436 *repos_relpath = svn_relpath_join(repos_parent_relpath, name, result_pool);
10438 return SVN_NO_ERROR;
10441 /* Helper for svn_wc__db_global_commit()
10443 Makes local_relpath and all its descendants at the same op-depth represent
10444 the copy origin repos_id:repos_relpath@revision.
10446 This code is only valid to fix-up a move from an old location, to a new
10447 location during a commit.
10450 * local_relpath is not the working copy root (can't be moved)
10451 * repos_relpath is not the repository root (can't be moved)
10453 static svn_error_t *
10454 moved_descendant_commit(svn_wc__db_wcroot_t *wcroot,
10455 const char *local_relpath,
10457 apr_int64_t repos_id,
10458 const char *repos_relpath,
10459 svn_revnum_t revision,
10460 apr_pool_t *scratch_pool)
10462 apr_hash_t *children;
10463 apr_pool_t *iterpool;
10464 svn_sqlite__stmt_t *stmt;
10465 svn_boolean_t have_row;
10466 apr_hash_index_t *hi;
10468 SVN_ERR_ASSERT(*local_relpath != '\0'
10469 && *repos_relpath != '\0');
10471 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10472 STMT_SELECT_MOVED_DESCENDANTS));
10473 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
10477 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10479 return svn_error_trace(svn_sqlite__reset(stmt));
10481 children = apr_hash_make(scratch_pool);
10483 /* First, obtain all moved children */
10484 /* To keep error handling simple, first cache them in a hashtable */
10487 const char *src_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
10488 const char *to_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
10490 svn_hash_sets(children, src_relpath, to_relpath);
10492 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10494 SVN_ERR(svn_sqlite__reset(stmt));
10496 /* Then update them */
10497 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10498 STMT_COMMIT_UPDATE_ORIGIN));
10500 iterpool = svn_pool_create(scratch_pool);
10501 for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
10503 const char *src_relpath = svn__apr_hash_index_key(hi);
10504 const char *to_relpath = svn__apr_hash_index_val(hi);
10505 const char *new_repos_relpath;
10506 int to_op_depth = relpath_depth(to_relpath);
10509 svn_pool_clear(iterpool);
10511 SVN_ERR_ASSERT(to_op_depth > 0);
10513 new_repos_relpath = svn_relpath_join(
10515 svn_relpath_skip_ancestor(local_relpath,
10519 SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
10525 SVN_ERR(svn_sqlite__update(&affected, stmt));
10528 /* Enable in release code?
10529 Broken moves are not fatal yet, but this assertion would break
10531 SVN_ERR_ASSERT(affected >= 1); /* If this fails there is no move dest */
10534 SVN_ERR(moved_descendant_commit(wcroot, to_relpath, to_op_depth,
10535 repos_id, new_repos_relpath, revision,
10539 svn_pool_destroy(iterpool);
10540 return SVN_NO_ERROR;
10543 /* Helper for svn_wc__db_global_commit()
10545 Moves all nodes below LOCAL_RELPATH from op-depth OP_DEPTH to op-depth 0
10546 (BASE), setting their presence to 'not-present' if their presence wasn't
10549 Makes all nodes below LOCAL_RELPATH represent the descendants of repository
10550 location repos_id:repos_relpath@revision.
10553 * local_relpath is not the working copy root (can't be replaced)
10554 * repos_relpath is not the repository root (can't be replaced)
10556 static svn_error_t *
10557 descendant_commit(svn_wc__db_wcroot_t *wcroot,
10558 const char *local_relpath,
10560 apr_int64_t repos_id,
10561 const char *repos_relpath,
10562 svn_revnum_t revision,
10563 apr_pool_t *scratch_pool)
10565 svn_sqlite__stmt_t *stmt;
10567 SVN_ERR_ASSERT(*local_relpath != '\0'
10568 && *repos_relpath != '\0');
10570 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10571 STMT_COMMIT_DESCENDANTS_TO_BASE));
10573 SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
10580 SVN_ERR(svn_sqlite__update(NULL, stmt));
10582 return SVN_NO_ERROR;
10585 /* The body of svn_wc__db_global_commit().
10587 static svn_error_t *
10588 commit_node(svn_wc__db_wcroot_t *wcroot,
10589 const char *local_relpath,
10590 svn_revnum_t new_revision,
10591 svn_revnum_t changed_rev,
10592 apr_time_t changed_date,
10593 const char *changed_author,
10594 const svn_checksum_t *new_checksum,
10595 const apr_array_header_t *new_children,
10596 apr_hash_t *new_dav_cache,
10597 svn_boolean_t keep_changelist,
10598 svn_boolean_t no_unlock,
10599 const svn_skel_t *work_items,
10600 apr_pool_t *scratch_pool)
10602 svn_sqlite__stmt_t *stmt_info;
10603 svn_sqlite__stmt_t *stmt_act;
10604 svn_boolean_t have_act;
10605 svn_string_t prop_blob = { 0 };
10606 svn_string_t inherited_prop_blob = { 0 };
10607 const char *changelist = NULL;
10608 const char *parent_relpath;
10609 svn_wc__db_status_t new_presence;
10610 svn_node_kind_t new_kind;
10611 const char *new_depth_str = NULL;
10612 svn_sqlite__stmt_t *stmt;
10613 apr_int64_t repos_id;
10614 const char *repos_relpath;
10616 svn_wc__db_status_t old_presence;
10618 /* If we are adding a file or directory, then we need to get
10619 repository information from the parent node since "this node" does
10622 For existing nodes, we should retain the (potentially-switched)
10623 repository information. */
10624 SVN_ERR(determine_repos_info(&repos_id, &repos_relpath,
10625 wcroot, local_relpath,
10626 scratch_pool, scratch_pool));
10628 /* ### is it better to select only the data needed? */
10629 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
10630 STMT_SELECT_NODE_INFO));
10631 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
10632 SVN_ERR(svn_sqlite__step_row(stmt_info));
10634 SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
10635 STMT_SELECT_ACTUAL_NODE));
10636 SVN_ERR(svn_sqlite__bindf(stmt_act, "is",
10637 wcroot->wc_id, local_relpath));
10638 SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
10640 /* There should be something to commit! */
10642 op_depth = svn_sqlite__column_int(stmt_info, 0);
10644 /* Figure out the new node's kind. It will be whatever is in WORKING_NODE,
10645 or there will be a BASE_NODE that has it. */
10646 new_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
10648 /* What will the new depth be? */
10649 if (new_kind == svn_node_dir)
10650 new_depth_str = svn_sqlite__column_text(stmt_info, 11, scratch_pool);
10652 /* Check that the repository information is not being changed. */
10655 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 1));
10656 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 2));
10658 /* A commit cannot change these values. */
10659 SVN_ERR_ASSERT(repos_id == svn_sqlite__column_int64(stmt_info, 1));
10660 SVN_ERR_ASSERT(strcmp(repos_relpath,
10661 svn_sqlite__column_text(stmt_info, 2, NULL)) == 0);
10664 /* Find the appropriate new properties -- ACTUAL overrides any properties
10665 in WORKING that arrived as part of a copy/move.
10667 Note: we'll keep them as a big blob of data, rather than
10668 deserialize/serialize them. */
10670 prop_blob.data = svn_sqlite__column_blob(stmt_act, 1, &prop_blob.len,
10672 if (prop_blob.data == NULL)
10673 prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len,
10676 inherited_prop_blob.data = svn_sqlite__column_blob(stmt_info, 16,
10677 &inherited_prop_blob.len,
10680 if (keep_changelist && have_act)
10681 changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool);
10683 old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map);
10685 /* ### other stuff? */
10687 SVN_ERR(svn_sqlite__reset(stmt_info));
10688 SVN_ERR(svn_sqlite__reset(stmt_act));
10694 /* This removes all layers of this node and at the same time determines
10695 if we need to remove shadowed layers below our descendants. */
10697 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10698 STMT_DELETE_ALL_LAYERS));
10699 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10700 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
10702 if (affected_rows > 1)
10704 /* We commit a shadowing operation
10706 1) Remove all shadowed nodes
10707 2) And remove all nodes that have a base-deleted as lowest layer,
10708 because 1) removed that layer */
10710 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10711 STMT_DELETE_SHADOWED_RECURSIVE));
10713 SVN_ERR(svn_sqlite__bindf(stmt,
10719 SVN_ERR(svn_sqlite__step_done(stmt));
10722 /* Note that while these two calls look so similar that they might
10723 be integrated, they really affect a different op-depth and
10724 completely different nodes (via a different recursion pattern). */
10726 /* Collapse descendants of the current op_depth in layer 0 */
10727 SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth,
10728 repos_id, repos_relpath, new_revision,
10731 /* And make the recorded local moves represent moves of the node we just
10733 SVN_ERR(moved_descendant_commit(wcroot, local_relpath, 0,
10734 repos_id, repos_relpath, new_revision,
10737 /* This node is no longer modified, so no node was moved here */
10738 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10739 STMT_CLEAR_MOVED_TO_FROM_DEST));
10740 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
10743 SVN_ERR(svn_sqlite__step_done(stmt));
10746 /* Update or add the BASE_NODE row with all the new information. */
10748 if (*local_relpath == '\0')
10749 parent_relpath = NULL;
10751 parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
10753 /* Preserve any incomplete status */
10754 new_presence = (old_presence == svn_wc__db_status_incomplete
10755 ? svn_wc__db_status_incomplete
10756 : svn_wc__db_status_normal);
10758 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10759 STMT_APPLY_CHANGES_TO_BASE_NODE));
10760 /* symlink_target not yet used */
10761 SVN_ERR(svn_sqlite__bindf(stmt, "issisrtstrisnbn",
10762 wcroot->wc_id, local_relpath,
10767 presence_map, new_presence,
10769 kind_map, new_kind,
10773 prop_blob.data, prop_blob.len));
10775 SVN_ERR(svn_sqlite__bind_checksum(stmt, 13, new_checksum,
10777 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, new_dav_cache,
10779 if (inherited_prop_blob.data != NULL)
10781 SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data,
10782 inherited_prop_blob.len));
10785 SVN_ERR(svn_sqlite__step_done(stmt));
10789 if (keep_changelist && changelist != NULL)
10791 /* The user told us to keep the changelist. Replace the row in
10792 ACTUAL_NODE with the basic keys and the changelist. */
10793 SVN_ERR(svn_sqlite__get_statement(
10794 &stmt, wcroot->sdb,
10795 STMT_RESET_ACTUAL_WITH_CHANGELIST));
10796 SVN_ERR(svn_sqlite__bindf(stmt, "isss",
10797 wcroot->wc_id, local_relpath,
10798 svn_relpath_dirname(local_relpath,
10801 SVN_ERR(svn_sqlite__step_done(stmt));
10805 /* Toss the ACTUAL_NODE row. */
10806 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10807 STMT_DELETE_ACTUAL_NODE));
10808 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10809 SVN_ERR(svn_sqlite__step_done(stmt));
10813 if (new_kind == svn_node_dir)
10815 /* When committing a directory, we should have its new children. */
10816 /* ### one day. just not today. */
10818 SVN_ERR_ASSERT(new_children != NULL);
10821 /* ### process the children */
10826 svn_sqlite__stmt_t *lock_stmt;
10828 SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
10829 STMT_DELETE_LOCK_RECURSIVELY));
10830 SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
10831 SVN_ERR(svn_sqlite__step_done(lock_stmt));
10834 /* Install any work items into the queue, as part of this transaction. */
10835 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
10837 return SVN_NO_ERROR;
10842 svn_wc__db_global_commit(svn_wc__db_t *db,
10843 const char *local_abspath,
10844 svn_revnum_t new_revision,
10845 svn_revnum_t changed_revision,
10846 apr_time_t changed_date,
10847 const char *changed_author,
10848 const svn_checksum_t *new_checksum,
10849 const apr_array_header_t *new_children,
10850 apr_hash_t *new_dav_cache,
10851 svn_boolean_t keep_changelist,
10852 svn_boolean_t no_unlock,
10853 const svn_skel_t *work_items,
10854 apr_pool_t *scratch_pool)
10856 const char *local_relpath;
10857 svn_wc__db_wcroot_t *wcroot;
10859 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10860 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
10861 SVN_ERR_ASSERT(new_checksum == NULL || new_children == NULL);
10863 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10864 local_abspath, scratch_pool, scratch_pool));
10865 VERIFY_USABLE_WCROOT(wcroot);
10867 SVN_WC__DB_WITH_TXN(
10868 commit_node(wcroot, local_relpath,
10869 new_revision, changed_revision, changed_date, changed_author,
10870 new_checksum, new_children, new_dav_cache, keep_changelist,
10871 no_unlock, work_items, scratch_pool),
10874 /* We *totally* monkeyed the entries. Toss 'em. */
10875 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
10877 return SVN_NO_ERROR;
10882 svn_wc__db_global_update(svn_wc__db_t *db,
10883 const char *local_abspath,
10884 svn_node_kind_t new_kind,
10885 const char *new_repos_relpath,
10886 svn_revnum_t new_revision,
10887 const apr_hash_t *new_props,
10888 svn_revnum_t new_changed_rev,
10889 apr_time_t new_changed_date,
10890 const char *new_changed_author,
10891 const apr_array_header_t *new_children,
10892 const svn_checksum_t *new_checksum,
10893 const char *new_target,
10894 const apr_hash_t *new_dav_cache,
10895 const svn_skel_t *conflict,
10896 const svn_skel_t *work_items,
10897 apr_pool_t *scratch_pool)
10902 svn_wc__db_wcroot_t *wcroot;
10903 const char *local_relpath;
10905 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10906 /* ### allow NULL for NEW_REPOS_RELPATH to indicate "no change"? */
10907 SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
10908 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
10909 SVN_ERR_ASSERT(new_props != NULL);
10910 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_changed_rev));
10911 SVN_ERR_ASSERT((new_children != NULL
10912 && new_checksum == NULL
10913 && new_target == NULL)
10914 || (new_children == NULL
10915 && new_checksum != NULL
10916 && new_target == NULL)
10917 || (new_children == NULL
10918 && new_checksum == NULL
10919 && new_target != NULL));
10921 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10922 local_abspath, scratch_pool, scratch_pool));
10923 VERIFY_USABLE_WCROOT(wcroot);
10925 SVN_WC__DB_WITH_TXN(
10926 update_node(wcroot, local_relpath,
10927 new_repos_relpath, new_revision, new_props,
10928 new_changed_rev, new_changed_date, new_changed_author,
10929 new_children, new_checksum, new_target,
10930 conflict, work_items, scratch_pool),
10933 /* We *totally* monkeyed the entries. Toss 'em. */
10934 SVN_ERR(flush_entries(wcroot, local_abspath, scratch_pool));
10936 return SVN_NO_ERROR;
10940 /* Sets a base nodes revision, repository relative path, and/or inherited
10941 propertis. If LOCAL_ABSPATH's rev (REV) is valid, set its revision. If
10942 SET_REPOS_RELPATH is TRUE set its repository relative path to REPOS_RELPATH
10943 (and make sure its REPOS_ID is still valid). If IPROPS is not NULL set its
10944 inherited properties to IPROPS, if IPROPS is NULL then clear any the iprops
10945 cache for the base node.
10947 static svn_error_t *
10948 db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot,
10949 const char *local_relpath,
10950 apr_array_header_t *iprops,
10952 svn_boolean_t set_repos_relpath,
10953 const char *repos_relpath,
10954 apr_int64_t repos_id,
10955 apr_pool_t *scratch_pool)
10957 svn_sqlite__stmt_t *stmt;
10959 SVN_ERR(flush_entries(wcroot,
10960 svn_dirent_join(wcroot->abspath, local_relpath,
10962 svn_depth_empty, scratch_pool));
10965 if (SVN_IS_VALID_REVNUM(rev))
10967 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10968 STMT_UPDATE_BASE_REVISION));
10970 SVN_ERR(svn_sqlite__bindf(stmt, "isr", wcroot->wc_id, local_relpath,
10973 SVN_ERR(svn_sqlite__step_done(stmt));
10976 if (set_repos_relpath)
10978 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10979 STMT_UPDATE_BASE_REPOS));
10981 SVN_ERR(svn_sqlite__bindf(stmt, "isis", wcroot->wc_id, local_relpath,
10982 repos_id, repos_relpath));
10984 SVN_ERR(svn_sqlite__step_done(stmt));
10987 /* Set or clear iprops. */
10988 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10989 STMT_UPDATE_IPROP));
10990 SVN_ERR(svn_sqlite__bindf(stmt, "is",
10993 SVN_ERR(svn_sqlite__bind_iprops(stmt, 3, iprops, scratch_pool));
10994 SVN_ERR(svn_sqlite__step_done(stmt));
10996 return SVN_NO_ERROR;
10999 /* The main body of bump_revisions_post_update().
11001 * Tweak the information for LOCAL_RELPATH in WCROOT. If NEW_REPOS_RELPATH is
11002 * non-NULL update the entry to the new url specified by NEW_REPOS_RELPATH,
11003 * NEW_REPOS_ID. If NEW_REV is valid, make this the node's working revision.
11005 * If WCROOT_IPROPS is not NULL it is a hash mapping const char * absolute
11006 * working copy paths to depth-first ordered arrays of
11007 * svn_prop_inherited_item_t * structures. If the absolute path equivalent
11008 * of LOCAL_RELPATH exists in WCROOT_IPROPS, then set the hashed value as the
11009 * node's inherited properties.
11011 * Unless S_ROOT is TRUE the tweaks might cause the node for LOCAL_ABSPATH to
11012 * be removed from the WC; if IS_ROOT is TRUE this will not happen.
11014 static svn_error_t *
11015 bump_node_revision(svn_wc__db_wcroot_t *wcroot,
11016 const char *local_relpath,
11017 apr_int64_t new_repos_id,
11018 const char *new_repos_relpath,
11019 svn_revnum_t new_rev,
11021 apr_hash_t *exclude_relpaths,
11022 apr_hash_t *wcroot_iprops,
11023 svn_boolean_t is_root,
11024 svn_boolean_t skip_when_dir,
11026 apr_pool_t *scratch_pool)
11028 apr_pool_t *iterpool;
11029 const apr_array_header_t *children;
11031 svn_wc__db_status_t status;
11032 svn_node_kind_t db_kind;
11033 svn_revnum_t revision;
11034 const char *repos_relpath;
11035 apr_int64_t repos_id;
11036 svn_boolean_t set_repos_relpath = FALSE;
11037 svn_boolean_t update_root;
11038 svn_depth_t depth_below_here = depth;
11039 apr_array_header_t *iprops = NULL;
11041 /* Skip an excluded path and its descendants. */
11042 if (svn_hash_gets(exclude_relpaths, local_relpath))
11043 return SVN_NO_ERROR;
11045 SVN_ERR(svn_wc__db_base_get_info_internal(&status, &db_kind, &revision,
11046 &repos_relpath, &repos_id,
11047 NULL, NULL, NULL, NULL, NULL,
11048 NULL, NULL, NULL, NULL, &update_root,
11049 wcroot, local_relpath,
11050 scratch_pool, scratch_pool));
11052 /* Skip file externals */
11054 && db_kind == svn_node_file
11056 return SVN_NO_ERROR;
11058 if (skip_when_dir && db_kind == svn_node_dir)
11059 return SVN_NO_ERROR;
11061 /* If the node is still marked 'not-present', then the server did not
11062 re-add it. So it's really gone in this revision, thus we remove the node.
11064 If the node is still marked 'server-excluded' and yet is not the same
11065 revision as new_rev, then the server did not re-add it, nor
11066 re-server-exclude it, so we can remove the node. */
11068 && (status == svn_wc__db_status_not_present
11069 || (status == svn_wc__db_status_server_excluded &&
11070 revision != new_rev)))
11072 return svn_error_trace(db_base_remove(wcroot, local_relpath,
11073 db, FALSE, FALSE, FALSE,
11074 SVN_INVALID_REVNUM,
11075 NULL, NULL, scratch_pool));
11078 if (new_repos_relpath != NULL && strcmp(repos_relpath, new_repos_relpath))
11079 set_repos_relpath = TRUE;
11082 iprops = svn_hash_gets(wcroot_iprops,
11083 svn_dirent_join(wcroot->abspath, local_relpath,
11087 || set_repos_relpath
11088 || (SVN_IS_VALID_REVNUM(new_rev) && new_rev != revision))
11090 SVN_ERR(db_op_set_rev_repos_relpath_iprops(wcroot, local_relpath,
11099 if (depth <= svn_depth_empty
11100 || db_kind != svn_node_dir
11101 || status == svn_wc__db_status_server_excluded
11102 || status == svn_wc__db_status_excluded
11103 || status == svn_wc__db_status_not_present)
11104 return SVN_NO_ERROR;
11106 /* And now recurse over the children */
11108 depth_below_here = depth;
11110 if (depth == svn_depth_immediates || depth == svn_depth_files)
11111 depth_below_here = svn_depth_empty;
11113 iterpool = svn_pool_create(scratch_pool);
11115 SVN_ERR(gather_repo_children(&children, wcroot, local_relpath, 0,
11116 scratch_pool, iterpool));
11117 for (i = 0; i < children->nelts; i++)
11119 const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
11120 const char *child_local_relpath;
11121 const char *child_repos_relpath = NULL;
11123 svn_pool_clear(iterpool);
11125 /* Derive the new URL for the current (child) entry */
11126 if (new_repos_relpath)
11127 child_repos_relpath = svn_relpath_join(new_repos_relpath,
11128 child_basename, iterpool);
11130 child_local_relpath = svn_relpath_join(local_relpath, child_basename,
11133 SVN_ERR(bump_node_revision(wcroot, child_local_relpath, new_repos_id,
11134 child_repos_relpath, new_rev,
11136 exclude_relpaths, wcroot_iprops,
11137 FALSE /* is_root */,
11138 (depth < svn_depth_immediates), db,
11143 svn_pool_destroy(iterpool);
11145 return SVN_NO_ERROR;
11148 /* Helper for svn_wc__db_op_bump_revisions_post_update().
11150 static svn_error_t *
11151 bump_revisions_post_update(svn_wc__db_wcroot_t *wcroot,
11152 const char *local_relpath,
11155 const char *new_repos_relpath,
11156 const char *new_repos_root_url,
11157 const char *new_repos_uuid,
11158 svn_revnum_t new_revision,
11159 apr_hash_t *exclude_relpaths,
11160 apr_hash_t *wcroot_iprops,
11161 svn_wc_notify_func2_t notify_func,
11162 void *notify_baton,
11163 apr_pool_t *scratch_pool)
11165 svn_wc__db_status_t status;
11166 svn_node_kind_t kind;
11168 apr_int64_t new_repos_id = INVALID_REPOS_ID;
11170 err = svn_wc__db_base_get_info_internal(&status, &kind, NULL, NULL, NULL,
11171 NULL, NULL, NULL, NULL, NULL, NULL,
11172 NULL, NULL, NULL, NULL,
11173 wcroot, local_relpath,
11174 scratch_pool, scratch_pool);
11175 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
11177 svn_error_clear(err);
11178 return SVN_NO_ERROR;
11185 case svn_wc__db_status_excluded:
11186 case svn_wc__db_status_server_excluded:
11187 case svn_wc__db_status_not_present:
11188 return SVN_NO_ERROR;
11190 /* Explicitly ignore other statii */
11195 if (new_repos_root_url != NULL)
11196 SVN_ERR(create_repos_id(&new_repos_id, new_repos_root_url,
11198 wcroot->sdb, scratch_pool));
11200 SVN_ERR(bump_node_revision(wcroot, local_relpath, new_repos_id,
11201 new_repos_relpath, new_revision,
11202 depth, exclude_relpaths,
11204 TRUE /* is_root */, FALSE, db,
11207 SVN_ERR(svn_wc__db_bump_moved_away(wcroot, local_relpath, depth, db,
11210 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, SVN_INVALID_REVNUM,
11211 SVN_INVALID_REVNUM, notify_func,
11212 notify_baton, scratch_pool));
11214 return SVN_NO_ERROR;
11218 svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db,
11219 const char *local_abspath,
11221 const char *new_repos_relpath,
11222 const char *new_repos_root_url,
11223 const char *new_repos_uuid,
11224 svn_revnum_t new_revision,
11225 apr_hash_t *exclude_relpaths,
11226 apr_hash_t *wcroot_iprops,
11227 svn_wc_notify_func2_t notify_func,
11228 void *notify_baton,
11229 apr_pool_t *scratch_pool)
11231 const char *local_relpath;
11232 svn_wc__db_wcroot_t *wcroot;
11234 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11235 local_abspath, scratch_pool, scratch_pool));
11237 VERIFY_USABLE_WCROOT(wcroot);
11239 if (svn_hash_gets(exclude_relpaths, local_relpath))
11240 return SVN_NO_ERROR;
11242 if (depth == svn_depth_unknown)
11243 depth = svn_depth_infinity;
11245 SVN_WC__DB_WITH_TXN(
11246 bump_revisions_post_update(wcroot, local_relpath, db,
11247 depth, new_repos_relpath, new_repos_root_url,
11248 new_repos_uuid, new_revision,
11249 exclude_relpaths, wcroot_iprops,
11250 notify_func, notify_baton, scratch_pool),
11253 return SVN_NO_ERROR;
11256 /* The body of svn_wc__db_lock_add().
11258 static svn_error_t *
11259 lock_add_txn(svn_wc__db_wcroot_t *wcroot,
11260 const char *local_relpath,
11261 const svn_wc__db_lock_t *lock,
11262 apr_pool_t *scratch_pool)
11264 svn_sqlite__stmt_t *stmt;
11265 const char *repos_relpath;
11266 apr_int64_t repos_id;
11268 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11269 &repos_relpath, &repos_id,
11270 NULL, NULL, NULL, NULL, NULL,
11271 NULL, NULL, NULL, NULL, NULL,
11272 wcroot, local_relpath,
11273 scratch_pool, scratch_pool));
11275 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_LOCK));
11276 SVN_ERR(svn_sqlite__bindf(stmt, "iss",
11277 repos_id, repos_relpath, lock->token));
11279 if (lock->owner != NULL)
11280 SVN_ERR(svn_sqlite__bind_text(stmt, 4, lock->owner));
11282 if (lock->comment != NULL)
11283 SVN_ERR(svn_sqlite__bind_text(stmt, 5, lock->comment));
11285 if (lock->date != 0)
11286 SVN_ERR(svn_sqlite__bind_int64(stmt, 6, lock->date));
11288 SVN_ERR(svn_sqlite__insert(NULL, stmt));
11290 return SVN_NO_ERROR;
11295 svn_wc__db_lock_add(svn_wc__db_t *db,
11296 const char *local_abspath,
11297 const svn_wc__db_lock_t *lock,
11298 apr_pool_t *scratch_pool)
11300 svn_wc__db_wcroot_t *wcroot;
11301 const char *local_relpath;
11303 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11304 SVN_ERR_ASSERT(lock != NULL);
11306 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11307 local_abspath, scratch_pool, scratch_pool));
11308 VERIFY_USABLE_WCROOT(wcroot);
11310 SVN_WC__DB_WITH_TXN(
11311 lock_add_txn(wcroot, local_relpath, lock, scratch_pool),
11314 /* There may be some entries, and the lock info is now out of date. */
11315 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
11317 return SVN_NO_ERROR;
11321 /* The body of svn_wc__db_lock_remove().
11323 static svn_error_t *
11324 lock_remove_txn(svn_wc__db_wcroot_t *wcroot,
11325 const char *local_relpath,
11326 apr_pool_t *scratch_pool)
11328 const char *repos_relpath;
11329 apr_int64_t repos_id;
11330 svn_sqlite__stmt_t *stmt;
11332 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11333 &repos_relpath, &repos_id,
11334 NULL, NULL, NULL, NULL, NULL,
11335 NULL, NULL, NULL, NULL, NULL,
11336 wcroot, local_relpath,
11337 scratch_pool, scratch_pool));
11339 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11340 STMT_DELETE_LOCK));
11341 SVN_ERR(svn_sqlite__bindf(stmt, "is", repos_id, repos_relpath));
11343 SVN_ERR(svn_sqlite__step_done(stmt));
11345 return SVN_NO_ERROR;
11350 svn_wc__db_lock_remove(svn_wc__db_t *db,
11351 const char *local_abspath,
11352 apr_pool_t *scratch_pool)
11354 svn_wc__db_wcroot_t *wcroot;
11355 const char *local_relpath;
11357 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11359 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11360 local_abspath, scratch_pool, scratch_pool));
11361 VERIFY_USABLE_WCROOT(wcroot);
11363 SVN_WC__DB_WITH_TXN(
11364 lock_remove_txn(wcroot, local_relpath, scratch_pool),
11367 /* There may be some entries, and the lock info is now out of date. */
11368 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
11370 return SVN_NO_ERROR;
11375 svn_wc__db_scan_base_repos(const char **repos_relpath,
11376 const char **repos_root_url,
11377 const char **repos_uuid,
11379 const char *local_abspath,
11380 apr_pool_t *result_pool,
11381 apr_pool_t *scratch_pool)
11383 svn_wc__db_wcroot_t *wcroot;
11384 const char *local_relpath;
11385 apr_int64_t repos_id;
11387 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11389 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11390 local_abspath, scratch_pool, scratch_pool));
11391 VERIFY_USABLE_WCROOT(wcroot);
11393 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11394 repos_relpath, &repos_id,
11395 NULL, NULL, NULL, NULL, NULL,
11396 NULL, NULL, NULL, NULL, NULL,
11397 wcroot, local_relpath,
11398 result_pool, scratch_pool));
11399 SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot->sdb,
11400 repos_id, result_pool));
11402 return SVN_NO_ERROR;
11406 /* A helper for scan_addition().
11407 * Compute moved-from information for the node at LOCAL_RELPATH which
11408 * has been determined as having been moved-here.
11409 * If MOVED_FROM_RELPATH is not NULL, set *MOVED_FROM_RELPATH to the
11410 * path of the move-source node in *MOVED_FROM_RELPATH.
11411 * If DELETE_OP_ROOT_RELPATH is not NULL, set *DELETE_OP_ROOT_RELPATH
11412 * to the path of the op-root of the delete-half of the move.
11413 * If moved-from information cannot be derived, set both *MOVED_FROM_RELPATH
11414 * and *DELETE_OP_ROOT_RELPATH to NULL, and return a "copied" status.
11415 * COPY_OPT_ROOT_RELPATH is the relpath of the op-root of the copied-half
11417 static svn_error_t *
11418 get_moved_from_info(const char **moved_from_relpath,
11419 const char **moved_from_op_root_relpath,
11420 const char *moved_to_op_root_relpath,
11422 svn_wc__db_wcroot_t *wcroot,
11423 const char *local_relpath,
11424 apr_pool_t *result_pool,
11425 apr_pool_t *scratch_pool)
11427 svn_sqlite__stmt_t *stmt;
11428 svn_boolean_t have_row;
11430 /* Run a query to get the moved-from path from the DB. */
11431 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11432 STMT_SELECT_MOVED_FROM_RELPATH));
11433 SVN_ERR(svn_sqlite__bindf(stmt, "is",
11434 wcroot->wc_id, moved_to_op_root_relpath));
11435 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11439 /* The move was only recorded at the copy-half, possibly because
11440 * the move operation was interrupted mid-way between the copy
11441 * and the delete. Treat this node as a normal copy. */
11442 if (moved_from_relpath)
11443 *moved_from_relpath = NULL;
11444 if (moved_from_op_root_relpath)
11445 *moved_from_op_root_relpath = NULL;
11447 SVN_ERR(svn_sqlite__reset(stmt));
11448 return SVN_NO_ERROR;
11452 *op_depth = svn_sqlite__column_int(stmt, 1);
11454 if (moved_from_relpath || moved_from_op_root_relpath)
11456 const char *db_delete_op_root_relpath;
11458 /* The moved-from path from the DB is the relpath of
11459 * the op_root of the delete-half of the move. */
11460 db_delete_op_root_relpath = svn_sqlite__column_text(stmt, 0,
11462 if (moved_from_op_root_relpath)
11463 *moved_from_op_root_relpath = db_delete_op_root_relpath;
11465 if (moved_from_relpath)
11467 if (strcmp(moved_to_op_root_relpath, local_relpath) == 0)
11469 /* LOCAL_RELPATH is the op_root of the copied-half of the
11470 * move, so the correct MOVED_FROM_ABSPATH is the op-root
11471 * of the delete-half. */
11472 *moved_from_relpath = db_delete_op_root_relpath;
11476 const char *child_relpath;
11478 /* LOCAL_RELPATH is a child that was copied along with the
11479 * op_root of the copied-half of the move. Construct the
11480 * corresponding path beneath the op_root of the delete-half. */
11482 /* Grab the child path relative to the op_root of the move
11484 child_relpath = svn_relpath_skip_ancestor(
11485 moved_to_op_root_relpath, local_relpath);
11487 SVN_ERR_ASSERT(child_relpath && strlen(child_relpath) > 0);
11489 /* This join is valid because LOCAL_RELPATH has not been moved
11490 * within the copied-half of the move yet -- else, it would
11491 * be its own op_root. */
11492 *moved_from_relpath = svn_relpath_join(db_delete_op_root_relpath,
11499 SVN_ERR(svn_sqlite__reset(stmt));
11501 return SVN_NO_ERROR;
11504 /* The body of scan_addition().
11506 static svn_error_t *
11507 scan_addition_txn(svn_wc__db_status_t *status,
11508 const char **op_root_relpath_p,
11509 const char **repos_relpath,
11510 apr_int64_t *repos_id,
11511 const char **original_repos_relpath,
11512 apr_int64_t *original_repos_id,
11513 svn_revnum_t *original_revision,
11514 const char **moved_from_relpath,
11515 const char **moved_from_op_root_relpath,
11516 int *moved_from_op_depth,
11517 svn_wc__db_wcroot_t *wcroot,
11518 const char *local_relpath,
11519 apr_pool_t *result_pool,
11520 apr_pool_t *scratch_pool)
11522 const char *op_root_relpath;
11523 const char *build_relpath = "";
11525 /* Initialize most of the OUT parameters. Generally, we'll only be filling
11526 in a subset of these, so it is easier to init all up front. Note that
11527 the STATUS parameter will be initialized once we read the status of
11528 the specified node. */
11529 if (op_root_relpath_p)
11530 *op_root_relpath_p = NULL;
11531 if (original_repos_relpath)
11532 *original_repos_relpath = NULL;
11533 if (original_repos_id)
11534 *original_repos_id = INVALID_REPOS_ID;
11535 if (original_revision)
11536 *original_revision = SVN_INVALID_REVNUM;
11537 if (moved_from_relpath)
11538 *moved_from_relpath = NULL;
11539 if (moved_from_op_root_relpath)
11540 *moved_from_op_root_relpath = NULL;
11541 if (moved_from_op_depth)
11542 *moved_from_op_depth = 0;
11545 svn_sqlite__stmt_t *stmt;
11546 svn_boolean_t have_row;
11547 svn_wc__db_status_t presence;
11549 const char *repos_prefix_path = "";
11552 /* ### is it faster to fetch fewer columns? */
11553 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11554 STMT_SELECT_WORKING_NODE));
11555 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11556 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11560 /* Reset statement before returning */
11561 SVN_ERR(svn_sqlite__reset(stmt));
11563 /* ### maybe we should return a usage error instead? */
11564 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
11565 _("The node '%s' was not found."),
11566 path_for_error_message(wcroot,
11571 presence = svn_sqlite__column_token(stmt, 1, presence_map);
11573 /* The starting node should exist normally. */
11574 op_depth = svn_sqlite__column_int(stmt, 0);
11575 if (op_depth == 0 || (presence != svn_wc__db_status_normal
11576 && presence != svn_wc__db_status_incomplete))
11577 /* reset the statement as part of the error generation process */
11578 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
11579 svn_sqlite__reset(stmt),
11580 _("Expected node '%s' to be added."),
11581 path_for_error_message(wcroot,
11585 if (original_revision)
11586 *original_revision = svn_sqlite__column_revnum(stmt, 12);
11588 /* Provide the default status; we'll override as appropriate. */
11591 if (presence == svn_wc__db_status_normal)
11592 *status = svn_wc__db_status_added;
11594 *status = svn_wc__db_status_incomplete;
11598 /* Calculate the op root local path components */
11599 op_root_relpath = local_relpath;
11601 for (i = relpath_depth(local_relpath); i > op_depth; --i)
11603 /* Calculate the path of the operation root */
11604 repos_prefix_path =
11605 svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
11608 op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
11611 if (op_root_relpath_p)
11612 *op_root_relpath_p = apr_pstrdup(result_pool, op_root_relpath);
11614 /* ### This if-statement is quite redundant.
11615 * ### We're checking all these values again within the body anyway.
11616 * ### The body should be broken up appropriately and move into the
11617 * ### outer scope. */
11618 if (original_repos_relpath
11619 || original_repos_id
11620 || (original_revision
11621 && *original_revision == SVN_INVALID_REVNUM)
11623 || moved_from_relpath || moved_from_op_root_relpath)
11625 if (local_relpath != op_root_relpath)
11626 /* requery to get the add/copy root */
11628 SVN_ERR(svn_sqlite__reset(stmt));
11630 SVN_ERR(svn_sqlite__bindf(stmt, "is",
11631 wcroot->wc_id, op_root_relpath));
11632 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11636 /* Reset statement before returning */
11637 SVN_ERR(svn_sqlite__reset(stmt));
11639 /* ### maybe we should return a usage error instead? */
11640 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
11641 _("The node '%s' was not found."),
11642 path_for_error_message(wcroot,
11647 if (original_revision
11648 && *original_revision == SVN_INVALID_REVNUM)
11649 *original_revision = svn_sqlite__column_revnum(stmt, 12);
11652 if (original_repos_relpath)
11653 *original_repos_relpath = svn_sqlite__column_text(stmt, 11,
11656 if (!svn_sqlite__column_is_null(stmt, 10)
11658 || original_repos_id
11659 || moved_from_relpath || moved_from_op_root_relpath))
11660 /* If column 10 (original_repos_id) is NULL,
11661 this is a plain add, not a copy or a move */
11663 svn_boolean_t moved_here;
11664 if (original_repos_id)
11665 *original_repos_id = svn_sqlite__column_int64(stmt, 10);
11667 moved_here = svn_sqlite__column_boolean(stmt, 13 /* moved_here */);
11669 *status = moved_here ? svn_wc__db_status_moved_here
11670 : svn_wc__db_status_copied;
11673 && (moved_from_relpath || moved_from_op_root_relpath))
11677 err = get_moved_from_info(moved_from_relpath,
11678 moved_from_op_root_relpath,
11680 moved_from_op_depth,
11681 wcroot, local_relpath,
11686 return svn_error_compose_create(
11687 err, svn_sqlite__reset(stmt));
11693 /* ### This loop here is to skip up to the first node which is a BASE node,
11694 because base_get_info() doesn't accommodate the scenario that
11695 we're looking at here; we found the true op_root, which may be inside
11696 further changed trees. */
11697 if (repos_relpath || repos_id)
11699 const char *base_relpath;
11704 SVN_ERR(svn_sqlite__reset(stmt));
11706 /* Pointing at op_depth, look at the parent */
11707 repos_prefix_path =
11708 svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
11711 op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
11714 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, op_root_relpath));
11715 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11720 op_depth = svn_sqlite__column_int(stmt, 0);
11722 /* Skip to op_depth */
11723 for (i = relpath_depth(op_root_relpath); i > op_depth; i--)
11725 /* Calculate the path of the operation root */
11726 repos_prefix_path =
11727 svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
11731 svn_relpath_dirname(op_root_relpath, scratch_pool);
11735 SVN_ERR(svn_sqlite__reset(stmt));
11737 build_relpath = repos_prefix_path;
11739 /* If we're here, then we have an added/copied/moved (start) node, and
11740 CURRENT_ABSPATH now points to a BASE node. Figure out the repository
11741 information for the current node, and use that to compute the start
11742 node's repository information. */
11743 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11744 &base_relpath, repos_id,
11745 NULL, NULL, NULL, NULL, NULL,
11746 NULL, NULL, NULL, NULL, NULL,
11747 wcroot, op_root_relpath,
11748 scratch_pool, scratch_pool));
11751 *repos_relpath = svn_relpath_join(base_relpath, build_relpath,
11755 SVN_ERR(svn_sqlite__reset(stmt));
11757 /* Postconditions */
11761 SVN_ERR_ASSERT(*status == svn_wc__db_status_added
11762 || *status == svn_wc__db_status_copied
11763 || *status == svn_wc__db_status_incomplete
11764 || *status == svn_wc__db_status_moved_here);
11765 if (*status == svn_wc__db_status_added)
11767 SVN_ERR_ASSERT(!original_repos_relpath
11768 || *original_repos_relpath == NULL);
11769 SVN_ERR_ASSERT(!original_revision
11770 || *original_revision == SVN_INVALID_REVNUM);
11771 SVN_ERR_ASSERT(!original_repos_id
11772 || *original_repos_id == INVALID_REPOS_ID);
11774 /* An upgrade with a missing directory can leave INCOMPLETE working
11775 op-roots. See upgrade_tests.py 29: upgrade with missing replaced dir
11777 else if (*status != svn_wc__db_status_incomplete)
11779 SVN_ERR_ASSERT(!original_repos_relpath
11780 || *original_repos_relpath != NULL);
11781 SVN_ERR_ASSERT(!original_revision
11782 || *original_revision != SVN_INVALID_REVNUM);
11783 SVN_ERR_ASSERT(!original_repos_id
11784 || *original_repos_id != INVALID_REPOS_ID);
11787 SVN_ERR_ASSERT(!op_root_relpath_p || *op_root_relpath_p != NULL);
11790 return SVN_NO_ERROR;
11794 /* Like svn_wc__db_scan_addition(), but with WCROOT+LOCAL_RELPATH instead of
11797 The output value of *ORIGINAL_REPOS_ID will be INVALID_REPOS_ID if there
11798 is no 'copy-from' repository. */
11799 static svn_error_t *
11800 scan_addition(svn_wc__db_status_t *status,
11801 const char **op_root_relpath,
11802 const char **repos_relpath,
11803 apr_int64_t *repos_id,
11804 const char **original_repos_relpath,
11805 apr_int64_t *original_repos_id,
11806 svn_revnum_t *original_revision,
11807 const char **moved_from_relpath,
11808 const char **moved_from_op_root_relpath,
11809 int *moved_from_op_depth,
11810 svn_wc__db_wcroot_t *wcroot,
11811 const char *local_relpath,
11812 apr_pool_t *result_pool,
11813 apr_pool_t *scratch_pool)
11815 SVN_WC__DB_WITH_TXN(
11816 scan_addition_txn(status, op_root_relpath, repos_relpath, repos_id,
11817 original_repos_relpath, original_repos_id,
11818 original_revision, moved_from_relpath,
11819 moved_from_op_root_relpath, moved_from_op_depth,
11820 wcroot, local_relpath, result_pool, scratch_pool),
11822 return SVN_NO_ERROR;
11827 svn_wc__db_scan_addition(svn_wc__db_status_t *status,
11828 const char **op_root_abspath,
11829 const char **repos_relpath,
11830 const char **repos_root_url,
11831 const char **repos_uuid,
11832 const char **original_repos_relpath,
11833 const char **original_root_url,
11834 const char **original_uuid,
11835 svn_revnum_t *original_revision,
11837 const char *local_abspath,
11838 apr_pool_t *result_pool,
11839 apr_pool_t *scratch_pool)
11841 svn_wc__db_wcroot_t *wcroot;
11842 const char *local_relpath;
11843 const char *op_root_relpath = NULL;
11844 apr_int64_t repos_id = INVALID_REPOS_ID;
11845 apr_int64_t original_repos_id = INVALID_REPOS_ID;
11846 apr_int64_t *repos_id_p
11847 = (repos_root_url || repos_uuid) ? &repos_id : NULL;
11848 apr_int64_t *original_repos_id_p
11849 = (original_root_url || original_uuid) ? &original_repos_id : NULL;
11851 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11853 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11854 local_abspath, scratch_pool, scratch_pool));
11855 VERIFY_USABLE_WCROOT(wcroot);
11857 SVN_ERR(scan_addition(status,
11861 repos_relpath, repos_id_p,
11862 original_repos_relpath, original_repos_id_p,
11865 wcroot, local_relpath, result_pool, scratch_pool));
11867 if (op_root_abspath)
11868 *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
11870 /* REPOS_ID must be valid if requested; ORIGINAL_REPOS_ID need not be. */
11871 SVN_ERR_ASSERT(repos_id_p == NULL || repos_id != INVALID_REPOS_ID);
11873 SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot->sdb,
11874 repos_id, result_pool));
11875 SVN_ERR(svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
11876 wcroot->sdb, original_repos_id,
11879 return SVN_NO_ERROR;
11883 svn_wc__db_scan_moved(const char **moved_from_abspath,
11884 const char **op_root_abspath,
11885 const char **op_root_moved_from_abspath,
11886 const char **moved_from_delete_abspath,
11888 const char *local_abspath,
11889 apr_pool_t *result_pool,
11890 apr_pool_t *scratch_pool)
11892 svn_wc__db_wcroot_t *wcroot;
11893 const char *local_relpath;
11894 svn_wc__db_status_t status;
11895 const char *op_root_relpath = NULL;
11896 const char *moved_from_relpath = NULL;
11897 const char *moved_from_op_root_relpath = NULL;
11898 int moved_from_op_depth = -1;
11900 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11902 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11903 local_abspath, scratch_pool, scratch_pool));
11904 VERIFY_USABLE_WCROOT(wcroot);
11906 SVN_ERR(scan_addition(&status,
11913 ? &moved_from_relpath
11915 (op_root_moved_from_abspath
11916 || moved_from_delete_abspath)
11917 ? &moved_from_op_root_relpath
11919 moved_from_delete_abspath
11920 ? &moved_from_op_depth
11922 wcroot, local_relpath, scratch_pool, scratch_pool));
11924 if (status != svn_wc__db_status_moved_here || !moved_from_relpath)
11925 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
11926 _("Path '%s' was not moved here"),
11927 path_for_error_message(wcroot, local_relpath,
11930 if (op_root_abspath)
11931 *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
11934 if (moved_from_abspath)
11935 *moved_from_abspath = svn_dirent_join(wcroot->abspath, moved_from_relpath,
11938 if (op_root_moved_from_abspath)
11939 *op_root_moved_from_abspath = svn_dirent_join(wcroot->abspath,
11940 moved_from_op_root_relpath,
11943 /* The deleted node is either where we moved from, or one of its ancestors */
11944 if (moved_from_delete_abspath)
11946 const char *tmp = moved_from_op_root_relpath;
11948 SVN_ERR_ASSERT(moved_from_op_depth >= 0);
11950 while (relpath_depth(tmp) > moved_from_op_depth)
11951 tmp = svn_relpath_dirname(tmp, scratch_pool);
11953 *moved_from_delete_abspath = svn_dirent_join(wcroot->abspath, tmp,
11957 return SVN_NO_ERROR;
11962 static svn_error_t *
11963 follow_moved_to(apr_array_header_t **moved_tos,
11965 const char *repos_path,
11966 svn_revnum_t revision,
11967 svn_wc__db_wcroot_t *wcroot,
11968 const char *local_relpath,
11969 apr_pool_t *result_pool,
11970 apr_pool_t *scratch_pool)
11972 svn_sqlite__stmt_t *stmt;
11973 svn_boolean_t have_row;
11974 int working_op_depth;
11975 const char *ancestor_relpath, *node_moved_to = NULL;
11978 SVN_ERR_ASSERT((!op_depth && !repos_path) || (op_depth && repos_path));
11980 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11981 STMT_SELECT_OP_DEPTH_MOVED_TO));
11982 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
11984 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11987 working_op_depth = svn_sqlite__column_int(stmt, 0);
11988 node_moved_to = svn_sqlite__column_text(stmt, 1, result_pool);
11991 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11992 if (!have_row || svn_sqlite__column_revnum(stmt, 0))
11993 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
11994 svn_sqlite__reset(stmt),
11995 _("The base node '%s' was not found."),
11996 path_for_error_message(wcroot,
11999 repos_path = svn_sqlite__column_text(stmt, 2, scratch_pool);
12000 revision = svn_sqlite__column_revnum(stmt, 3);
12003 SVN_ERR(svn_sqlite__reset(stmt));
12007 svn_boolean_t have_row2;
12009 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12010 STMT_SELECT_MOVED_HERE));
12011 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, node_moved_to,
12012 relpath_depth(node_moved_to)));
12013 SVN_ERR(svn_sqlite__step(&have_row2, stmt));
12014 if (!have_row2 || !svn_sqlite__column_int(stmt, 0)
12015 || revision != svn_sqlite__column_revnum(stmt, 3)
12016 || strcmp(repos_path, svn_sqlite__column_text(stmt, 2, NULL)))
12017 node_moved_to = NULL;
12018 SVN_ERR(svn_sqlite__reset(stmt));
12023 struct svn_wc__db_moved_to_t *moved_to;
12025 moved_to = apr_palloc(result_pool, sizeof(*moved_to));
12026 moved_to->op_depth = working_op_depth;
12027 moved_to->local_relpath = node_moved_to;
12028 APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
12031 /* A working row with moved_to, or no working row, and we are done. */
12032 if (node_moved_to || !have_row)
12033 return SVN_NO_ERROR;
12035 /* Need to handle being moved via an ancestor. */
12036 ancestor_relpath = local_relpath;
12037 for (i = relpath_depth(local_relpath); i > working_op_depth; --i)
12039 const char *ancestor_moved_to;
12041 ancestor_relpath = svn_relpath_dirname(ancestor_relpath, scratch_pool);
12043 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12044 STMT_SELECT_MOVED_TO));
12045 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, ancestor_relpath,
12046 working_op_depth));
12047 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12048 SVN_ERR_ASSERT(have_row);
12049 ancestor_moved_to = svn_sqlite__column_text(stmt, 0, scratch_pool);
12050 SVN_ERR(svn_sqlite__reset(stmt));
12051 if (ancestor_moved_to)
12054 = svn_relpath_join(ancestor_moved_to,
12055 svn_relpath_skip_ancestor(ancestor_relpath,
12059 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12060 STMT_SELECT_MOVED_HERE));
12061 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, node_moved_to,
12062 relpath_depth(ancestor_moved_to)));
12063 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12065 ancestor_moved_to = NULL;
12066 else if (!svn_sqlite__column_int(stmt, 0))
12068 svn_wc__db_status_t presence
12069 = svn_sqlite__column_token(stmt, 1, presence_map);
12070 if (presence != svn_wc__db_status_not_present)
12071 ancestor_moved_to = NULL;
12074 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12075 if (!have_row && !svn_sqlite__column_int(stmt, 0))
12076 ancestor_moved_to = NULL;
12079 SVN_ERR(svn_sqlite__reset(stmt));
12080 if (!ancestor_moved_to)
12082 /* verify repos_path points back? */
12084 if (ancestor_moved_to)
12086 struct svn_wc__db_moved_to_t *moved_to;
12088 moved_to = apr_palloc(result_pool, sizeof(*moved_to));
12089 moved_to->op_depth = working_op_depth;
12090 moved_to->local_relpath = node_moved_to;
12091 APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
12093 SVN_ERR(follow_moved_to(moved_tos, relpath_depth(ancestor_moved_to),
12094 repos_path, revision, wcroot, node_moved_to,
12095 result_pool, scratch_pool));
12100 return SVN_NO_ERROR;
12104 svn_wc__db_follow_moved_to(apr_array_header_t **moved_tos,
12106 const char *local_abspath,
12107 apr_pool_t *result_pool,
12108 apr_pool_t *scratch_pool)
12110 svn_wc__db_wcroot_t *wcroot;
12111 const char *local_relpath;
12113 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12115 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12116 local_abspath, scratch_pool, scratch_pool));
12117 VERIFY_USABLE_WCROOT(wcroot);
12119 *moved_tos = apr_array_make(result_pool, 0,
12120 sizeof(struct svn_wc__db_moved_to_t *));
12122 /* ### Wrap in a transaction */
12123 SVN_ERR(follow_moved_to(moved_tos, 0, NULL, SVN_INVALID_REVNUM,
12124 wcroot, local_relpath,
12125 result_pool, scratch_pool));
12127 /* ### Convert moved_to to abspath */
12129 return SVN_NO_ERROR;
12132 /* Extract the moved-to information for LOCAL_RELPATH at OP-DEPTH by
12133 examining the lowest working node above OP_DEPTH. The output paths
12134 are NULL if there is no move, otherwise:
12136 *MOVE_DST_RELPATH: the moved-to destination of LOCAL_RELPATH.
12138 *MOVE_DST_OP_ROOT_RELPATH: the moved-to destination of the root of
12139 the move of LOCAL_RELPATH. This may be equal to *MOVE_DST_RELPATH
12140 if LOCAL_RELPATH is the root of the move.
12142 *MOVE_SRC_ROOT_RELPATH: the root of the move source. For moves
12143 inside a delete this will be different from *MOVE_SRC_OP_ROOT_RELPATH.
12145 *MOVE_SRC_OP_ROOT_RELPATH: the root of the source layer that
12146 contains the move. For moves inside deletes this is the root of
12147 the delete, for other moves this is the root of the move.
12149 Given a path A/B/C with A/B moved to X then for A/B/C
12151 MOVE_DST_RELPATH is X/C
12152 MOVE_DST_OP_ROOT_RELPATH is X
12153 MOVE_SRC_ROOT_RELPATH is A/B
12154 MOVE_SRC_OP_ROOT_RELPATH is A/B
12156 If A is then deleted the MOVE_DST_RELPATH, MOVE_DST_OP_ROOT_RELPATH
12157 and MOVE_SRC_ROOT_RELPATH remain the same but MOVE_SRC_OP_ROOT_RELPATH
12160 ### Think about combining with scan_deletion? Also with
12161 ### scan_addition to get moved-to for replaces? Do we need to
12162 ### return the op-root of the move source, i.e. A/B in the example
12165 svn_wc__db_op_depth_moved_to(const char **move_dst_relpath,
12166 const char **move_dst_op_root_relpath,
12167 const char **move_src_root_relpath,
12168 const char **move_src_op_root_relpath,
12170 svn_wc__db_wcroot_t *wcroot,
12171 const char *local_relpath,
12172 apr_pool_t *result_pool,
12173 apr_pool_t *scratch_pool)
12175 svn_sqlite__stmt_t *stmt;
12176 svn_boolean_t have_row;
12177 int delete_op_depth;
12178 const char *relpath = local_relpath;
12180 *move_dst_relpath = *move_dst_op_root_relpath = NULL;
12181 *move_src_root_relpath = *move_src_op_root_relpath = NULL;
12185 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12186 STMT_SELECT_LOWEST_WORKING_NODE));
12187 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath, op_depth));
12188 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12191 delete_op_depth = svn_sqlite__column_int(stmt, 0);
12192 *move_dst_op_root_relpath = svn_sqlite__column_text(stmt, 3,
12194 if (*move_dst_op_root_relpath)
12195 *move_src_root_relpath = apr_pstrdup(result_pool, relpath);
12197 SVN_ERR(svn_sqlite__reset(stmt));
12198 if (!*move_dst_op_root_relpath)
12199 relpath = svn_relpath_dirname(relpath, scratch_pool);
12201 while (!*move_dst_op_root_relpath
12202 && have_row && delete_op_depth <= relpath_depth(relpath));
12204 if (*move_dst_op_root_relpath)
12207 = svn_relpath_join(*move_dst_op_root_relpath,
12208 svn_relpath_skip_ancestor(relpath, local_relpath),
12210 while (delete_op_depth < relpath_depth(relpath))
12211 relpath = svn_relpath_dirname(relpath, scratch_pool);
12212 *move_src_op_root_relpath = apr_pstrdup(result_pool, relpath);
12215 return SVN_NO_ERROR;
12218 /* Public (within libsvn_wc) absolute path version of
12219 svn_wc__db_op_depth_moved_to with the op-depth hard-coded to
12222 svn_wc__db_base_moved_to(const char **move_dst_abspath,
12223 const char **move_dst_op_root_abspath,
12224 const char **move_src_root_abspath,
12225 const char **move_src_op_root_abspath,
12227 const char *local_abspath,
12228 apr_pool_t *result_pool,
12229 apr_pool_t *scratch_pool)
12231 svn_wc__db_wcroot_t *wcroot;
12232 const char *local_relpath;
12233 const char *move_dst_relpath, *move_dst_op_root_relpath;
12234 const char *move_src_root_relpath, *move_src_op_root_relpath;
12236 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12238 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12239 local_abspath, scratch_pool, scratch_pool));
12240 VERIFY_USABLE_WCROOT(wcroot);
12242 SVN_WC__DB_WITH_TXN(svn_wc__db_op_depth_moved_to(&move_dst_relpath,
12243 &move_dst_op_root_relpath,
12244 &move_src_root_relpath,
12245 &move_src_op_root_relpath,
12246 0 /* BASE op-depth */,
12247 wcroot, local_relpath,
12248 scratch_pool, scratch_pool),
12251 if (move_dst_abspath)
12254 ? svn_dirent_join(wcroot->abspath, move_dst_relpath, result_pool)
12257 if (move_dst_op_root_abspath)
12258 *move_dst_op_root_abspath
12259 = move_dst_op_root_relpath
12260 ? svn_dirent_join(wcroot->abspath, move_dst_op_root_relpath, result_pool)
12263 if (move_src_root_abspath)
12264 *move_src_root_abspath
12265 = move_src_root_relpath
12266 ? svn_dirent_join(wcroot->abspath, move_src_root_relpath, result_pool)
12269 if (move_src_op_root_abspath)
12270 *move_src_op_root_abspath
12271 = move_src_op_root_relpath
12272 ? svn_dirent_join(wcroot->abspath, move_src_op_root_relpath, result_pool)
12275 return SVN_NO_ERROR;
12279 svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb,
12280 apr_int64_t *repos_id,
12281 apr_int64_t *wc_id,
12282 svn_wc__db_t *wc_db,
12283 const char *dir_abspath,
12284 const char *repos_root_url,
12285 const char *repos_uuid,
12286 apr_pool_t *scratch_pool)
12288 svn_wc__db_wcroot_t *wcroot;
12290 /* Upgrade is inherently exclusive so specify exclusive locking. */
12291 SVN_ERR(create_db(sdb, repos_id, wc_id, dir_abspath,
12292 repos_root_url, repos_uuid,
12294 NULL, SVN_INVALID_REVNUM, svn_depth_unknown,
12295 TRUE /* exclusive */,
12296 wc_db->state_pool, scratch_pool));
12298 SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
12299 apr_pstrdup(wc_db->state_pool,
12301 *sdb, *wc_id, FORMAT_FROM_SDB,
12302 FALSE /* auto-upgrade */,
12303 FALSE /* enforce_empty_wq */,
12304 wc_db->state_pool, scratch_pool));
12306 /* The WCROOT is complete. Stash it into DB. */
12307 svn_hash_sets(wc_db->dir_data, wcroot->abspath, wcroot);
12309 return SVN_NO_ERROR;
12314 svn_wc__db_upgrade_apply_dav_cache(svn_sqlite__db_t *sdb,
12315 const char *dir_relpath,
12316 apr_hash_t *cache_values,
12317 apr_pool_t *scratch_pool)
12319 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
12321 apr_hash_index_t *hi;
12322 svn_sqlite__stmt_t *stmt;
12324 SVN_ERR(svn_wc__db_util_fetch_wc_id(&wc_id, sdb, iterpool));
12326 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12327 STMT_UPDATE_BASE_NODE_DAV_CACHE));
12329 /* Iterate over all the wcprops, writing each one to the wc_db. */
12330 for (hi = apr_hash_first(scratch_pool, cache_values);
12332 hi = apr_hash_next(hi))
12334 const char *name = svn__apr_hash_index_key(hi);
12335 apr_hash_t *props = svn__apr_hash_index_val(hi);
12336 const char *local_relpath;
12338 svn_pool_clear(iterpool);
12340 local_relpath = svn_relpath_join(dir_relpath, name, iterpool);
12342 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
12343 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, iterpool));
12344 SVN_ERR(svn_sqlite__step_done(stmt));
12347 svn_pool_destroy(iterpool);
12349 return SVN_NO_ERROR;
12354 svn_wc__db_upgrade_apply_props(svn_sqlite__db_t *sdb,
12355 const char *dir_abspath,
12356 const char *local_relpath,
12357 apr_hash_t *base_props,
12358 apr_hash_t *revert_props,
12359 apr_hash_t *working_props,
12360 int original_format,
12362 apr_pool_t *scratch_pool)
12364 svn_sqlite__stmt_t *stmt;
12365 svn_boolean_t have_row;
12366 int top_op_depth = -1;
12367 int below_op_depth = -1;
12368 svn_wc__db_status_t top_presence;
12369 svn_wc__db_status_t below_presence;
12372 /* ### working_props: use set_props_txn.
12373 ### if working_props == NULL, then skip. what if they equal the
12374 ### pristine props? we should probably do the compare here.
12376 ### base props go into WORKING_NODE if avail, otherwise BASE.
12378 ### revert only goes into BASE. (and WORKING better be there!)
12380 Prior to 1.4.0 (ORIGINAL_FORMAT < 8), REVERT_PROPS did not exist. If a
12381 file was deleted, then a copy (potentially with props) was disallowed
12382 and could not replace the deletion. An addition *could* be performed,
12383 but that would never bring its own props.
12385 1.4.0 through 1.4.5 created the concept of REVERT_PROPS, but had a
12386 bug in svn_wc_add_repos_file2() whereby a copy-with-props did NOT
12387 construct a REVERT_PROPS if the target had no props. Thus, reverting
12388 the delete/copy would see no REVERT_PROPS to restore, leaving the
12389 props from the copy source intact, and appearing as if they are (now)
12390 the base props for the previously-deleted file. (wc corruption)
12392 1.4.6 ensured that an empty REVERT_PROPS would be established at all
12393 times. See issue 2530, and r861670 as starting points.
12395 We will use ORIGINAL_FORMAT and SVN_WC__NO_REVERT_FILES to determine
12396 the handling of our inputs, relative to the state of this node.
12399 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_NODE_INFO));
12400 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
12401 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12404 top_op_depth = svn_sqlite__column_int(stmt, 0);
12405 top_presence = svn_sqlite__column_token(stmt, 3, presence_map);
12406 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12409 below_op_depth = svn_sqlite__column_int(stmt, 0);
12410 below_presence = svn_sqlite__column_token(stmt, 3, presence_map);
12413 SVN_ERR(svn_sqlite__reset(stmt));
12415 /* Detect the buggy scenario described above. We cannot upgrade this
12416 working copy if we have no idea where BASE_PROPS should go. */
12417 if (original_format > SVN_WC__NO_REVERT_FILES
12418 && revert_props == NULL
12419 && top_op_depth != -1
12420 && top_presence == svn_wc__db_status_normal
12421 && below_op_depth != -1
12422 && below_presence != svn_wc__db_status_not_present)
12424 /* There should be REVERT_PROPS, so it appears that we just ran into
12425 the described bug. Sigh. */
12426 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
12427 _("The properties of '%s' are in an "
12428 "indeterminate state and cannot be "
12429 "upgraded. See issue #2530."),
12430 svn_dirent_local_style(
12431 svn_dirent_join(dir_abspath, local_relpath,
12432 scratch_pool), scratch_pool));
12435 /* Need at least one row, or two rows if there are revert props */
12436 if (top_op_depth == -1
12437 || (below_op_depth == -1 && revert_props))
12438 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
12439 _("Insufficient NODES rows for '%s'"),
12440 svn_dirent_local_style(
12441 svn_dirent_join(dir_abspath, local_relpath,
12442 scratch_pool), scratch_pool));
12444 /* one row, base props only: upper row gets base props
12445 two rows, base props only: lower row gets base props
12446 two rows, revert props only: lower row gets revert props
12447 two rows, base and revert props: upper row gets base, lower gets revert */
12450 if (revert_props || below_op_depth == -1)
12452 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12453 STMT_UPDATE_NODE_PROPS));
12454 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
12455 wc_id, local_relpath, top_op_depth));
12456 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, base_props, scratch_pool));
12457 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
12459 SVN_ERR_ASSERT(affected_rows == 1);
12462 if (below_op_depth != -1)
12464 apr_hash_t *props = revert_props ? revert_props : base_props;
12466 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12467 STMT_UPDATE_NODE_PROPS));
12468 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
12469 wc_id, local_relpath, below_op_depth));
12470 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
12471 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
12473 SVN_ERR_ASSERT(affected_rows == 1);
12476 /* If there are WORKING_PROPS, then they always go into ACTUAL_NODE. */
12477 if (working_props != NULL
12478 && base_props != NULL)
12480 apr_array_header_t *diffs;
12482 SVN_ERR(svn_prop_diffs(&diffs, working_props, base_props, scratch_pool));
12484 if (diffs->nelts == 0)
12485 working_props = NULL; /* No differences */
12488 if (working_props != NULL)
12490 SVN_ERR(set_actual_props(wc_id, local_relpath, working_props,
12491 sdb, scratch_pool));
12494 return SVN_NO_ERROR;
12498 svn_wc__db_upgrade_insert_external(svn_wc__db_t *db,
12499 const char *local_abspath,
12500 svn_node_kind_t kind,
12501 const char *parent_abspath,
12502 const char *def_local_abspath,
12503 const char *repos_relpath,
12504 const char *repos_root_url,
12505 const char *repos_uuid,
12506 svn_revnum_t def_peg_revision,
12507 svn_revnum_t def_revision,
12508 apr_pool_t *scratch_pool)
12510 svn_wc__db_wcroot_t *wcroot;
12511 const char *def_local_relpath;
12512 svn_sqlite__stmt_t *stmt;
12513 svn_boolean_t have_row;
12514 apr_int64_t repos_id;
12516 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12518 /* We know only of DEF_LOCAL_ABSPATH that it definitely belongs to "this"
12519 * WC, i.e. where the svn:externals prop is set. The external target path
12520 * itself may be "hidden behind" other working copies. */
12521 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &def_local_relpath,
12522 db, def_local_abspath,
12523 scratch_pool, scratch_pool));
12524 VERIFY_USABLE_WCROOT(wcroot);
12527 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12528 STMT_SELECT_REPOSITORY));
12529 SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
12530 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12533 repos_id = svn_sqlite__column_int64(stmt, 0);
12534 SVN_ERR(svn_sqlite__reset(stmt));
12538 /* Need to set up a new repository row. */
12539 SVN_ERR(create_repos_id(&repos_id, repos_root_url, repos_uuid,
12540 wcroot->sdb, scratch_pool));
12543 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12544 STMT_INSERT_EXTERNAL));
12546 /* wc_id, local_relpath, parent_relpath, presence, kind, def_local_relpath,
12547 * repos_id, def_repos_relpath, def_operational_revision, def_revision */
12548 SVN_ERR(svn_sqlite__bindf(stmt, "issstsis",
12550 svn_dirent_skip_ancestor(wcroot->abspath,
12552 svn_dirent_skip_ancestor(wcroot->abspath,
12560 if (SVN_IS_VALID_REVNUM(def_peg_revision))
12561 SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, def_peg_revision));
12563 if (SVN_IS_VALID_REVNUM(def_revision))
12564 SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, def_revision));
12566 SVN_ERR(svn_sqlite__insert(NULL, stmt));
12568 return SVN_NO_ERROR;
12572 svn_wc__db_upgrade_get_repos_id(apr_int64_t *repos_id,
12573 svn_sqlite__db_t *sdb,
12574 const char *repos_root_url,
12575 apr_pool_t *scratch_pool)
12577 svn_sqlite__stmt_t *stmt;
12578 svn_boolean_t have_row;
12580 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_REPOSITORY));
12581 SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
12582 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12585 return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt),
12586 _("Repository '%s' not found in the database"),
12589 *repos_id = svn_sqlite__column_int64(stmt, 0);
12590 return svn_error_trace(svn_sqlite__reset(stmt));
12595 svn_wc__db_wq_add(svn_wc__db_t *db,
12596 const char *wri_abspath,
12597 const svn_skel_t *work_item,
12598 apr_pool_t *scratch_pool)
12600 svn_wc__db_wcroot_t *wcroot;
12601 const char *local_relpath;
12603 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
12605 /* Quick exit, if there are no work items to queue up. */
12606 if (work_item == NULL)
12607 return SVN_NO_ERROR;
12609 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12610 wri_abspath, scratch_pool, scratch_pool));
12611 VERIFY_USABLE_WCROOT(wcroot);
12613 /* Add the work item(s) to the WORK_QUEUE. */
12614 return svn_error_trace(add_work_items(wcroot->sdb, work_item,
12618 /* The body of svn_wc__db_wq_fetch_next().
12620 static svn_error_t *
12621 wq_fetch_next(apr_uint64_t *id,
12622 svn_skel_t **work_item,
12623 svn_wc__db_wcroot_t *wcroot,
12624 const char *local_relpath,
12625 apr_uint64_t completed_id,
12626 apr_pool_t *result_pool,
12627 apr_pool_t *scratch_pool)
12629 svn_sqlite__stmt_t *stmt;
12630 svn_boolean_t have_row;
12632 if (completed_id != 0)
12634 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12635 STMT_DELETE_WORK_ITEM));
12636 SVN_ERR(svn_sqlite__bind_int64(stmt, 1, completed_id));
12638 SVN_ERR(svn_sqlite__step_done(stmt));
12641 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12642 STMT_SELECT_WORK_ITEM));
12643 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12655 *id = svn_sqlite__column_int64(stmt, 0);
12657 val = svn_sqlite__column_blob(stmt, 1, &len, result_pool);
12659 *work_item = svn_skel__parse(val, len, result_pool);
12662 return svn_error_trace(svn_sqlite__reset(stmt));
12666 svn_wc__db_wq_fetch_next(apr_uint64_t *id,
12667 svn_skel_t **work_item,
12669 const char *wri_abspath,
12670 apr_uint64_t completed_id,
12671 apr_pool_t *result_pool,
12672 apr_pool_t *scratch_pool)
12674 svn_wc__db_wcroot_t *wcroot;
12675 const char *local_relpath;
12677 SVN_ERR_ASSERT(id != NULL);
12678 SVN_ERR_ASSERT(work_item != NULL);
12679 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
12681 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12682 wri_abspath, scratch_pool, scratch_pool));
12683 VERIFY_USABLE_WCROOT(wcroot);
12685 SVN_WC__DB_WITH_TXN(
12686 wq_fetch_next(id, work_item,
12687 wcroot, local_relpath, completed_id,
12688 result_pool, scratch_pool),
12691 return SVN_NO_ERROR;
12694 /* Records timestamp and date for one or more files in wcroot */
12695 static svn_error_t *
12696 wq_record(svn_wc__db_wcroot_t *wcroot,
12697 apr_hash_t *record_map,
12698 apr_pool_t *scratch_pool)
12700 apr_hash_index_t *hi;
12701 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
12703 for (hi = apr_hash_first(scratch_pool, record_map); hi;
12704 hi = apr_hash_next(hi))
12706 const char *local_abspath = svn__apr_hash_index_key(hi);
12707 const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
12708 const char *local_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
12711 svn_pool_clear(iterpool);
12713 if (! local_relpath)
12716 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
12717 dirent->filesize, dirent->mtime,
12721 svn_pool_destroy(iterpool);
12722 return SVN_NO_ERROR;
12726 svn_wc__db_wq_record_and_fetch_next(apr_uint64_t *id,
12727 svn_skel_t **work_item,
12729 const char *wri_abspath,
12730 apr_uint64_t completed_id,
12731 apr_hash_t *record_map,
12732 apr_pool_t *result_pool,
12733 apr_pool_t *scratch_pool)
12735 svn_wc__db_wcroot_t *wcroot;
12736 const char *local_relpath;
12738 SVN_ERR_ASSERT(id != NULL);
12739 SVN_ERR_ASSERT(work_item != NULL);
12740 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
12742 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12743 wri_abspath, scratch_pool, scratch_pool));
12744 VERIFY_USABLE_WCROOT(wcroot);
12746 SVN_WC__DB_WITH_TXN(
12747 svn_error_compose_create(
12748 wq_fetch_next(id, work_item,
12749 wcroot, local_relpath, completed_id,
12750 result_pool, scratch_pool),
12751 wq_record(wcroot, record_map, scratch_pool)),
12754 return SVN_NO_ERROR;
12759 /* ### temporary API. remove before release. */
12761 svn_wc__db_temp_get_format(int *format,
12763 const char *local_dir_abspath,
12764 apr_pool_t *scratch_pool)
12766 svn_wc__db_wcroot_t *wcroot;
12767 const char *local_relpath;
12770 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
12771 /* ### assert that we were passed a directory? */
12773 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12774 local_dir_abspath, scratch_pool, scratch_pool);
12776 /* If we hit an error examining this directory, then declare this
12777 directory to not be a working copy. */
12780 if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
12781 return svn_error_trace(err);
12782 svn_error_clear(err);
12784 /* Remap the returned error. */
12786 return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
12787 _("'%s' is not a working copy"),
12788 svn_dirent_local_style(local_dir_abspath,
12792 SVN_ERR_ASSERT(wcroot != NULL);
12793 SVN_ERR_ASSERT(wcroot->format >= 1);
12795 *format = wcroot->format;
12797 return SVN_NO_ERROR;
12800 /* ### temporary API. remove before release. */
12801 svn_wc_adm_access_t *
12802 svn_wc__db_temp_get_access(svn_wc__db_t *db,
12803 const char *local_dir_abspath,
12804 apr_pool_t *scratch_pool)
12806 const char *local_relpath;
12807 svn_wc__db_wcroot_t *wcroot;
12810 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
12812 /* ### we really need to assert that we were passed a directory. sometimes
12813 ### adm_retrieve_internal is asked about a file, and then it asks us
12814 ### for an access baton for it. we should definitely return NULL, but
12815 ### ideally: the caller would never ask us about a non-directory. */
12817 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
12818 db, local_dir_abspath, scratch_pool, scratch_pool);
12821 svn_error_clear(err);
12828 return svn_hash_gets(wcroot->access_cache, local_dir_abspath);
12832 /* ### temporary API. remove before release. */
12834 svn_wc__db_temp_set_access(svn_wc__db_t *db,
12835 const char *local_dir_abspath,
12836 svn_wc_adm_access_t *adm_access,
12837 apr_pool_t *scratch_pool)
12839 const char *local_relpath;
12840 svn_wc__db_wcroot_t *wcroot;
12843 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
12844 /* ### assert that we were passed a directory? */
12846 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
12847 db, local_dir_abspath, scratch_pool, scratch_pool);
12850 /* We don't even have a wcroot, so just bail. */
12851 svn_error_clear(err);
12855 /* Better not override something already there. */
12856 SVN_ERR_ASSERT_NO_RETURN(
12857 svn_hash_gets(wcroot->access_cache, local_dir_abspath) == NULL
12859 svn_hash_sets(wcroot->access_cache, local_dir_abspath, adm_access);
12863 /* ### temporary API. remove before release. */
12865 svn_wc__db_temp_close_access(svn_wc__db_t *db,
12866 const char *local_dir_abspath,
12867 svn_wc_adm_access_t *adm_access,
12868 apr_pool_t *scratch_pool)
12870 const char *local_relpath;
12871 svn_wc__db_wcroot_t *wcroot;
12873 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
12874 /* ### assert that we were passed a directory? */
12876 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12877 local_dir_abspath, scratch_pool, scratch_pool));
12878 svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
12880 return SVN_NO_ERROR;
12884 /* ### temporary API. remove before release. */
12886 svn_wc__db_temp_clear_access(svn_wc__db_t *db,
12887 const char *local_dir_abspath,
12888 apr_pool_t *scratch_pool)
12890 const char *local_relpath;
12891 svn_wc__db_wcroot_t *wcroot;
12894 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
12895 /* ### assert that we were passed a directory? */
12897 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
12898 db, local_dir_abspath, scratch_pool, scratch_pool);
12901 svn_error_clear(err);
12905 svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
12910 svn_wc__db_temp_get_all_access(svn_wc__db_t *db,
12911 apr_pool_t *result_pool)
12913 apr_hash_t *result = apr_hash_make(result_pool);
12914 apr_hash_index_t *hi;
12916 for (hi = apr_hash_first(result_pool, db->dir_data);
12918 hi = apr_hash_next(hi))
12920 const svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi);
12922 /* This is highly redundant, 'cause the same WCROOT will appear many
12923 times in dir_data. */
12924 result = apr_hash_overlay(result_pool, result, wcroot->access_cache);
12932 svn_wc__db_temp_borrow_sdb(svn_sqlite__db_t **sdb,
12934 const char *local_dir_abspath,
12935 apr_pool_t *scratch_pool)
12937 svn_wc__db_wcroot_t *wcroot;
12938 const char *local_relpath;
12940 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
12942 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12943 local_dir_abspath, scratch_pool, scratch_pool));
12944 VERIFY_USABLE_WCROOT(wcroot);
12946 *sdb = wcroot->sdb;
12948 return SVN_NO_ERROR;
12953 svn_wc__db_read_conflict_victims(const apr_array_header_t **victims,
12955 const char *local_abspath,
12956 apr_pool_t *result_pool,
12957 apr_pool_t *scratch_pool)
12959 svn_wc__db_wcroot_t *wcroot;
12960 const char *local_relpath;
12961 svn_sqlite__stmt_t *stmt;
12962 svn_boolean_t have_row;
12963 apr_array_header_t *new_victims;
12965 /* The parent should be a working copy directory. */
12966 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12967 local_abspath, scratch_pool, scratch_pool));
12968 VERIFY_USABLE_WCROOT(wcroot);
12970 /* ### This will be much easier once we have all conflicts in one
12973 /* Look for text, tree and property conflicts in ACTUAL */
12974 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12975 STMT_SELECT_CONFLICT_VICTIMS));
12976 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
12978 new_victims = apr_array_make(result_pool, 0, sizeof(const char *));
12980 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12983 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
12985 APR_ARRAY_PUSH(new_victims, const char *) =
12986 svn_relpath_basename(child_relpath, result_pool);
12988 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12991 SVN_ERR(svn_sqlite__reset(stmt));
12993 *victims = new_victims;
12994 return SVN_NO_ERROR;
12997 /* The body of svn_wc__db_get_conflict_marker_files().
12999 static svn_error_t *
13000 get_conflict_marker_files(apr_hash_t **marker_files_p,
13001 svn_wc__db_wcroot_t *wcroot,
13002 const char *local_relpath,
13004 apr_pool_t *result_pool,
13005 apr_pool_t *scratch_pool)
13007 svn_sqlite__stmt_t *stmt;
13008 svn_boolean_t have_row;
13009 apr_hash_t *marker_files = apr_hash_make(result_pool);
13011 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13012 STMT_SELECT_ACTUAL_NODE));
13013 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13014 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13016 if (have_row && !svn_sqlite__column_is_null(stmt, 2))
13019 const void *data = svn_sqlite__column_blob(stmt, 2, &len, NULL);
13020 svn_skel_t *conflicts;
13021 const apr_array_header_t *markers;
13024 conflicts = svn_skel__parse(data, len, scratch_pool);
13026 /* ### ADD markers to *marker_files */
13027 SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13029 result_pool, scratch_pool));
13031 for (i = 0; markers && (i < markers->nelts); i++)
13033 const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13035 svn_hash_sets(marker_files, marker_abspath, "");
13038 SVN_ERR(svn_sqlite__reset(stmt));
13040 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13041 STMT_SELECT_CONFLICT_VICTIMS));
13042 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13043 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13048 const void *data = svn_sqlite__column_blob(stmt, 1, &len, NULL);
13050 const apr_array_header_t *markers;
13055 svn_skel_t *conflicts;
13056 conflicts = svn_skel__parse(data, len, scratch_pool);
13058 SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13060 result_pool, scratch_pool));
13062 for (i = 0; markers && (i < markers->nelts); i++)
13064 const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13066 svn_hash_sets(marker_files, marker_abspath, "");
13070 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13073 if (apr_hash_count(marker_files))
13074 *marker_files_p = marker_files;
13076 *marker_files_p = NULL;
13078 return svn_error_trace(svn_sqlite__reset(stmt));
13082 svn_wc__db_get_conflict_marker_files(apr_hash_t **marker_files,
13084 const char *local_abspath,
13085 apr_pool_t *result_pool,
13086 apr_pool_t *scratch_pool)
13088 svn_wc__db_wcroot_t *wcroot;
13089 const char *local_relpath;
13091 /* The parent should be a working copy directory. */
13092 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13093 local_abspath, scratch_pool, scratch_pool));
13094 VERIFY_USABLE_WCROOT(wcroot);
13096 SVN_WC__DB_WITH_TXN(
13097 get_conflict_marker_files(marker_files, wcroot, local_relpath, db,
13098 result_pool, scratch_pool),
13101 return SVN_NO_ERROR;
13106 svn_wc__db_read_conflict(svn_skel_t **conflict,
13108 const char *local_abspath,
13109 apr_pool_t *result_pool,
13110 apr_pool_t *scratch_pool)
13112 svn_wc__db_wcroot_t *wcroot;
13113 const char *local_relpath;
13115 /* The parent should be a working copy directory. */
13116 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13117 local_abspath, scratch_pool, scratch_pool));
13118 VERIFY_USABLE_WCROOT(wcroot);
13120 return svn_error_trace(svn_wc__db_read_conflict_internal(conflict, wcroot,
13127 svn_wc__db_read_conflict_internal(svn_skel_t **conflict,
13128 svn_wc__db_wcroot_t *wcroot,
13129 const char *local_relpath,
13130 apr_pool_t *result_pool,
13131 apr_pool_t *scratch_pool)
13133 svn_sqlite__stmt_t *stmt;
13134 svn_boolean_t have_row;
13136 /* Check if we have a conflict in ACTUAL */
13137 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13138 STMT_SELECT_ACTUAL_NODE));
13139 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13141 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13145 /* Do this while stmt is still open to avoid closing the sqlite
13146 transaction and then reopening. */
13147 svn_sqlite__stmt_t *stmt_node;
13150 err = svn_sqlite__get_statement(&stmt_node, wcroot->sdb,
13151 STMT_SELECT_NODE_INFO);
13156 err = svn_sqlite__bindf(stmt_node, "is", wcroot->wc_id,
13160 err = svn_sqlite__step(&have_row, stmt_node);
13163 err = svn_error_compose_create(err,
13164 svn_sqlite__reset(stmt_node));
13166 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
13171 return SVN_NO_ERROR;
13174 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13175 _("The node '%s' was not found."),
13176 path_for_error_message(wcroot,
13182 apr_size_t cfl_len;
13183 const void *cfl_data;
13185 /* svn_skel__parse doesn't copy data, so store in result_pool */
13186 cfl_data = svn_sqlite__column_blob(stmt, 2, &cfl_len, result_pool);
13189 *conflict = svn_skel__parse(cfl_data, cfl_len, result_pool);
13193 return svn_error_trace(svn_sqlite__reset(stmt));
13199 svn_wc__db_read_kind(svn_node_kind_t *kind,
13201 const char *local_abspath,
13202 svn_boolean_t allow_missing,
13203 svn_boolean_t show_deleted,
13204 svn_boolean_t show_hidden,
13205 apr_pool_t *scratch_pool)
13207 svn_wc__db_wcroot_t *wcroot;
13208 const char *local_relpath;
13209 svn_sqlite__stmt_t *stmt_info;
13210 svn_boolean_t have_info;
13212 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13214 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13215 local_abspath, scratch_pool, scratch_pool));
13216 VERIFY_USABLE_WCROOT(wcroot);
13218 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
13219 STMT_SELECT_NODE_INFO));
13220 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
13221 SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
13227 *kind = svn_node_unknown;
13228 SVN_ERR(svn_sqlite__reset(stmt_info));
13229 return SVN_NO_ERROR;
13233 SVN_ERR(svn_sqlite__reset(stmt_info));
13234 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13235 _("The node '%s' was not found."),
13236 path_for_error_message(wcroot,
13242 if (!(show_deleted && show_hidden))
13244 int op_depth = svn_sqlite__column_int(stmt_info, 0);
13245 svn_boolean_t report_none = FALSE;
13246 svn_wc__db_status_t status = svn_sqlite__column_token(stmt_info, 3,
13250 SVN_ERR(convert_to_working_status(&status, status));
13254 case svn_wc__db_status_not_present:
13255 if (! (show_hidden && show_deleted))
13256 report_none = TRUE;
13258 case svn_wc__db_status_excluded:
13259 case svn_wc__db_status_server_excluded:
13261 report_none = TRUE;
13263 case svn_wc__db_status_deleted:
13264 if (! show_deleted)
13265 report_none = TRUE;
13273 *kind = svn_node_none;
13274 return svn_error_trace(svn_sqlite__reset(stmt_info));
13278 *kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
13280 return svn_error_trace(svn_sqlite__reset(stmt_info));
13285 svn_wc__db_node_hidden(svn_boolean_t *hidden,
13287 const char *local_abspath,
13288 apr_pool_t *scratch_pool)
13290 svn_wc__db_wcroot_t *wcroot;
13291 const char *local_relpath;
13292 svn_wc__db_status_t status;
13294 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13296 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13297 local_abspath, scratch_pool, scratch_pool));
13298 VERIFY_USABLE_WCROOT(wcroot);
13300 SVN_ERR(read_info(&status, NULL, NULL, NULL, NULL, NULL,
13301 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13302 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13304 wcroot, local_relpath,
13305 scratch_pool, scratch_pool));
13307 *hidden = (status == svn_wc__db_status_server_excluded
13308 || status == svn_wc__db_status_not_present
13309 || status == svn_wc__db_status_excluded);
13311 return SVN_NO_ERROR;
13316 svn_wc__db_is_wcroot(svn_boolean_t *is_wcroot,
13318 const char *local_abspath,
13319 apr_pool_t *scratch_pool)
13321 svn_wc__db_wcroot_t *wcroot;
13322 const char *local_relpath;
13324 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13326 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13327 local_abspath, scratch_pool, scratch_pool));
13328 VERIFY_USABLE_WCROOT(wcroot);
13330 if (*local_relpath != '\0')
13332 *is_wcroot = FALSE; /* Node is a file, or has a parent directory within
13334 return SVN_NO_ERROR;
13339 return SVN_NO_ERROR;
13342 /* Find a node's kind and whether it is switched, putting the outputs in
13343 * *IS_SWITCHED and *KIND. Either of the outputs may be NULL if not wanted.
13345 static svn_error_t *
13346 db_is_switched(svn_boolean_t *is_switched,
13347 svn_node_kind_t *kind,
13348 svn_wc__db_wcroot_t *wcroot,
13349 const char *local_relpath,
13350 apr_pool_t *scratch_pool)
13352 svn_wc__db_status_t status;
13353 apr_int64_t repos_id;
13354 const char *repos_relpath;
13356 const char *parent_local_relpath;
13357 apr_int64_t parent_repos_id;
13358 const char *parent_repos_relpath;
13360 SVN_ERR_ASSERT(*local_relpath != '\0'); /* Handled in wrapper */
13362 SVN_ERR(read_info(&status, kind, NULL, &repos_relpath, &repos_id, NULL,
13363 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13364 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13365 wcroot, local_relpath, scratch_pool, scratch_pool));
13367 if (status == svn_wc__db_status_server_excluded
13368 || status == svn_wc__db_status_excluded
13369 || status == svn_wc__db_status_not_present)
13371 return svn_error_createf(
13372 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13373 _("The node '%s' was not found."),
13374 path_for_error_message(wcroot, local_relpath,
13377 else if (! repos_relpath)
13379 /* Node is shadowed; easy out */
13381 *is_switched = FALSE;
13383 return SVN_NO_ERROR;
13387 return SVN_NO_ERROR;
13389 svn_relpath_split(&parent_local_relpath, &name, local_relpath, scratch_pool);
13391 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
13392 &parent_repos_relpath,
13393 &parent_repos_id, NULL, NULL, NULL,
13394 NULL, NULL, NULL, NULL, NULL,
13396 wcroot, parent_local_relpath,
13397 scratch_pool, scratch_pool));
13399 if (repos_id != parent_repos_id)
13400 *is_switched = TRUE;
13403 const char *expected_relpath;
13405 expected_relpath = svn_relpath_join(parent_repos_relpath, name,
13408 *is_switched = (strcmp(expected_relpath, repos_relpath) != 0);
13411 return SVN_NO_ERROR;
13415 svn_wc__db_is_switched(svn_boolean_t *is_wcroot,
13416 svn_boolean_t *is_switched,
13417 svn_node_kind_t *kind,
13419 const char *local_abspath,
13420 apr_pool_t *scratch_pool)
13422 svn_wc__db_wcroot_t *wcroot;
13423 const char *local_relpath;
13425 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13427 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13428 local_abspath, scratch_pool, scratch_pool));
13429 VERIFY_USABLE_WCROOT(wcroot);
13432 *is_switched = FALSE;
13434 if (*local_relpath == '\0')
13441 *kind = svn_node_dir;
13442 return SVN_NO_ERROR;
13446 *is_wcroot = FALSE;
13448 if (! is_switched && ! kind)
13449 return SVN_NO_ERROR;
13451 SVN_WC__DB_WITH_TXN(
13452 db_is_switched(is_switched, kind, wcroot, local_relpath, scratch_pool),
13454 return SVN_NO_ERROR;
13459 svn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath,
13461 const char *wri_abspath,
13462 apr_pool_t *result_pool,
13463 apr_pool_t *scratch_pool)
13465 svn_wc__db_wcroot_t *wcroot;
13466 const char *local_relpath;
13468 SVN_ERR_ASSERT(temp_dir_abspath != NULL);
13469 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13471 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13472 wri_abspath, scratch_pool, scratch_pool));
13473 VERIFY_USABLE_WCROOT(wcroot);
13475 *temp_dir_abspath = svn_dirent_join_many(result_pool,
13477 svn_wc_get_adm_dir(scratch_pool),
13478 WCROOT_TEMPDIR_RELPATH,
13480 return SVN_NO_ERROR;
13484 /* Helper for wclock_obtain_cb() to steal an existing lock */
13485 static svn_error_t *
13486 wclock_steal(svn_wc__db_wcroot_t *wcroot,
13487 const char *local_relpath,
13488 apr_pool_t *scratch_pool)
13490 svn_sqlite__stmt_t *stmt;
13492 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_WC_LOCK));
13493 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13495 SVN_ERR(svn_sqlite__step_done(stmt));
13497 return SVN_NO_ERROR;
13501 /* The body of svn_wc__db_wclock_obtain().
13503 static svn_error_t *
13504 wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot,
13505 const char *local_relpath,
13506 int levels_to_lock,
13507 svn_boolean_t steal_lock,
13508 apr_pool_t *scratch_pool)
13510 svn_sqlite__stmt_t *stmt;
13512 const char *lock_relpath;
13515 svn_boolean_t got_row;
13517 svn_wc__db_wclock_t lock;
13519 /* Upgrade locks the root before the node exists. Apart from that
13520 the root node always exists so we will just skip the check.
13522 ### Perhaps the lock for upgrade should be created when the db is
13523 created? 1.6 used to lock .svn on creation. */
13524 if (local_relpath[0])
13526 svn_boolean_t exists;
13528 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
13530 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13531 _("The node '%s' was not found."),
13532 path_for_error_message(wcroot,
13537 /* Check if there are nodes locked below the new lock root */
13538 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_FIND_WC_LOCK));
13539 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13541 lock_depth = relpath_depth(local_relpath);
13542 max_depth = lock_depth + levels_to_lock;
13544 SVN_ERR(svn_sqlite__step(&got_row, stmt));
13548 svn_boolean_t own_lock;
13550 lock_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
13552 /* If we are not locking with depth infinity, check if this lock
13553 voids our lock request */
13554 if (levels_to_lock >= 0
13555 && relpath_depth(lock_relpath) > max_depth)
13557 SVN_ERR(svn_sqlite__step(&got_row, stmt));
13561 /* Check if we are the lock owner, because we should be able to
13562 extend our lock. */
13563 err = wclock_owns_lock(&own_lock, wcroot, lock_relpath,
13564 TRUE, scratch_pool);
13567 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
13569 if (!own_lock && !steal_lock)
13571 SVN_ERR(svn_sqlite__reset(stmt));
13572 err = svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
13573 _("'%s' is already locked."),
13574 path_for_error_message(wcroot,
13577 return svn_error_createf(SVN_ERR_WC_LOCKED, err,
13578 _("Working copy '%s' locked."),
13579 path_for_error_message(wcroot,
13583 else if (!own_lock)
13585 err = wclock_steal(wcroot, lock_relpath, scratch_pool);
13588 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
13591 SVN_ERR(svn_sqlite__step(&got_row, stmt));
13594 SVN_ERR(svn_sqlite__reset(stmt));
13597 SVN_ERR(wclock_steal(wcroot, local_relpath, scratch_pool));
13599 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_WC_LOCK));
13600 lock_relpath = local_relpath;
13604 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, lock_relpath));
13606 SVN_ERR(svn_sqlite__step(&got_row, stmt));
13610 int levels = svn_sqlite__column_int(stmt, 0);
13612 levels += relpath_depth(lock_relpath);
13614 SVN_ERR(svn_sqlite__reset(stmt));
13616 if (levels == -1 || levels >= lock_depth)
13619 err = svn_error_createf(
13620 SVN_ERR_WC_LOCKED, NULL,
13621 _("'%s' is already locked."),
13622 svn_dirent_local_style(
13623 svn_dirent_join(wcroot->abspath,
13627 return svn_error_createf(
13628 SVN_ERR_WC_LOCKED, err,
13629 _("Working copy '%s' locked."),
13630 path_for_error_message(wcroot,
13635 break; /* There can't be interesting locks on higher nodes */
13638 SVN_ERR(svn_sqlite__reset(stmt));
13640 if (!*lock_relpath)
13643 lock_relpath = svn_relpath_dirname(lock_relpath, scratch_pool);
13646 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_WC_LOCK));
13647 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
13649 err = svn_sqlite__insert(NULL, stmt);
13651 return svn_error_createf(SVN_ERR_WC_LOCKED, err,
13652 _("Working copy '%s' locked"),
13653 path_for_error_message(wcroot,
13657 /* And finally store that we obtained the lock */
13658 lock.local_relpath = apr_pstrdup(wcroot->owned_locks->pool, local_relpath);
13659 lock.levels = levels_to_lock;
13660 APR_ARRAY_PUSH(wcroot->owned_locks, svn_wc__db_wclock_t) = lock;
13662 return SVN_NO_ERROR;
13667 svn_wc__db_wclock_obtain(svn_wc__db_t *db,
13668 const char *local_abspath,
13669 int levels_to_lock,
13670 svn_boolean_t steal_lock,
13671 apr_pool_t *scratch_pool)
13673 svn_wc__db_wcroot_t *wcroot;
13674 const char *local_relpath;
13676 SVN_ERR_ASSERT(levels_to_lock >= -1);
13677 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13679 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13681 scratch_pool, scratch_pool));
13682 VERIFY_USABLE_WCROOT(wcroot);
13687 int depth = relpath_depth(local_relpath);
13689 for (i = 0; i < wcroot->owned_locks->nelts; i++)
13691 svn_wc__db_wclock_t* lock = &APR_ARRAY_IDX(wcroot->owned_locks,
13692 i, svn_wc__db_wclock_t);
13694 if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
13695 && (lock->levels == -1
13696 || (lock->levels + relpath_depth(lock->local_relpath))
13699 return svn_error_createf(
13700 SVN_ERR_WC_LOCKED, NULL,
13701 _("'%s' is already locked via '%s'."),
13702 svn_dirent_local_style(local_abspath, scratch_pool),
13703 path_for_error_message(wcroot, lock->local_relpath,
13709 SVN_WC__DB_WITH_TXN(
13710 wclock_obtain_cb(wcroot, local_relpath, levels_to_lock, steal_lock,
13713 return SVN_NO_ERROR;
13717 /* The body of svn_wc__db_wclock_find_root() and svn_wc__db_wclocked(). */
13718 static svn_error_t *
13719 find_wclock(const char **lock_relpath,
13720 svn_wc__db_wcroot_t *wcroot,
13721 const char *dir_relpath,
13722 apr_pool_t *result_pool,
13723 apr_pool_t *scratch_pool)
13725 svn_sqlite__stmt_t *stmt;
13726 svn_boolean_t have_row;
13727 int dir_depth = relpath_depth(dir_relpath);
13728 const char *first_relpath;
13730 /* Check for locks on all directories that might be ancestors.
13731 As our new apis only use recursive locks the number of locks stored
13732 in the DB will be very low */
13733 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13734 STMT_SELECT_ANCESTOR_WCLOCKS));
13736 /* Get the top level relpath to reduce the worst case number of results
13737 to the number of directories below this node plus two.
13738 (1: the node itself and 2: the wcroot). */
13739 first_relpath = strchr(dir_relpath, '/');
13741 if (first_relpath != NULL)
13742 first_relpath = apr_pstrndup(scratch_pool, dir_relpath,
13743 first_relpath - dir_relpath);
13745 first_relpath = dir_relpath;
13747 SVN_ERR(svn_sqlite__bindf(stmt, "iss",
13752 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13756 const char *relpath = svn_sqlite__column_text(stmt, 0, NULL);
13758 if (svn_relpath_skip_ancestor(relpath, dir_relpath))
13760 int locked_levels = svn_sqlite__column_int(stmt, 1);
13761 int row_depth = relpath_depth(relpath);
13763 if (locked_levels == -1
13764 || locked_levels + row_depth >= dir_depth)
13766 *lock_relpath = apr_pstrdup(result_pool, relpath);
13767 SVN_ERR(svn_sqlite__reset(stmt));
13768 return SVN_NO_ERROR;
13772 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13775 *lock_relpath = NULL;
13777 return svn_error_trace(svn_sqlite__reset(stmt));
13780 static svn_error_t *
13781 is_wclocked(svn_boolean_t *locked,
13782 svn_wc__db_wcroot_t *wcroot,
13783 const char *dir_relpath,
13784 apr_pool_t *scratch_pool)
13786 const char *lock_relpath;
13788 SVN_ERR(find_wclock(&lock_relpath, wcroot, dir_relpath,
13789 scratch_pool, scratch_pool));
13790 *locked = (lock_relpath != NULL);
13791 return SVN_NO_ERROR;
13796 svn_wc__db_wclock_find_root(const char **lock_abspath,
13798 const char *local_abspath,
13799 apr_pool_t *result_pool,
13800 apr_pool_t *scratch_pool)
13802 svn_wc__db_wcroot_t *wcroot;
13803 const char *local_relpath;
13804 const char *lock_relpath;
13806 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13807 local_abspath, scratch_pool, scratch_pool));
13808 VERIFY_USABLE_WCROOT(wcroot);
13810 SVN_WC__DB_WITH_TXN(
13811 find_wclock(&lock_relpath, wcroot, local_relpath,
13812 scratch_pool, scratch_pool),
13816 *lock_abspath = NULL;
13818 SVN_ERR(svn_wc__db_from_relpath(lock_abspath, db, wcroot->abspath,
13819 lock_relpath, result_pool, scratch_pool));
13820 return SVN_NO_ERROR;
13825 svn_wc__db_wclocked(svn_boolean_t *locked,
13827 const char *local_abspath,
13828 apr_pool_t *scratch_pool)
13830 svn_wc__db_wcroot_t *wcroot;
13831 const char *local_relpath;
13833 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13834 local_abspath, scratch_pool, scratch_pool));
13835 VERIFY_USABLE_WCROOT(wcroot);
13837 SVN_WC__DB_WITH_TXN(
13838 is_wclocked(locked, wcroot, local_relpath, scratch_pool),
13841 return SVN_NO_ERROR;
13846 svn_wc__db_wclock_release(svn_wc__db_t *db,
13847 const char *local_abspath,
13848 apr_pool_t *scratch_pool)
13850 svn_sqlite__stmt_t *stmt;
13851 svn_wc__db_wcroot_t *wcroot;
13852 const char *local_relpath;
13854 apr_array_header_t *owned_locks;
13856 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13857 local_abspath, scratch_pool, scratch_pool));
13859 VERIFY_USABLE_WCROOT(wcroot);
13861 /* First check and remove the owns-lock information as failure in
13862 removing the db record implies that we have to steal the lock later. */
13863 owned_locks = wcroot->owned_locks;
13864 for (i = 0; i < owned_locks->nelts; i++)
13866 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
13867 svn_wc__db_wclock_t);
13869 if (strcmp(lock->local_relpath, local_relpath) == 0)
13873 if (i >= owned_locks->nelts)
13874 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
13875 _("Working copy not locked at '%s'."),
13876 svn_dirent_local_style(local_abspath,
13879 if (i < owned_locks->nelts)
13881 owned_locks->nelts--;
13883 /* Move the last item in the array to the deleted place */
13884 if (owned_locks->nelts > 0)
13885 APR_ARRAY_IDX(owned_locks, i, svn_wc__db_wclock_t) =
13886 APR_ARRAY_IDX(owned_locks, owned_locks->nelts, svn_wc__db_wclock_t);
13889 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13890 STMT_DELETE_WC_LOCK));
13892 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13894 SVN_ERR(svn_sqlite__step_done(stmt));
13896 return SVN_NO_ERROR;
13900 /* Like svn_wc__db_wclock_owns_lock() but taking WCROOT+LOCAL_RELPATH instead
13901 of DB+LOCAL_ABSPATH. */
13902 static svn_error_t *
13903 wclock_owns_lock(svn_boolean_t *own_lock,
13904 svn_wc__db_wcroot_t *wcroot,
13905 const char *local_relpath,
13906 svn_boolean_t exact,
13907 apr_pool_t *scratch_pool)
13909 apr_array_header_t *owned_locks;
13914 owned_locks = wcroot->owned_locks;
13915 lock_level = relpath_depth(local_relpath);
13919 for (i = 0; i < owned_locks->nelts; i++)
13921 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
13922 svn_wc__db_wclock_t);
13924 if (strcmp(lock->local_relpath, local_relpath) == 0)
13927 return SVN_NO_ERROR;
13933 for (i = 0; i < owned_locks->nelts; i++)
13935 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
13936 svn_wc__db_wclock_t);
13938 if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
13939 && (lock->levels == -1
13940 || ((relpath_depth(lock->local_relpath) + lock->levels)
13944 return SVN_NO_ERROR;
13949 return SVN_NO_ERROR;
13954 svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
13956 const char *local_abspath,
13957 svn_boolean_t exact,
13958 apr_pool_t *scratch_pool)
13960 svn_wc__db_wcroot_t *wcroot;
13961 const char *local_relpath;
13963 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13964 local_abspath, scratch_pool, scratch_pool));
13967 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
13968 _("The node '%s' was not found."),
13969 svn_dirent_local_style(local_abspath,
13972 VERIFY_USABLE_WCROOT(wcroot);
13974 SVN_ERR(wclock_owns_lock(own_lock, wcroot, local_relpath, exact,
13977 return SVN_NO_ERROR;
13980 /* The body of svn_wc__db_temp_op_end_directory_update().
13982 static svn_error_t *
13983 end_directory_update(svn_wc__db_wcroot_t *wcroot,
13984 const char *local_relpath,
13985 apr_pool_t *scratch_pool)
13987 svn_sqlite__stmt_t *stmt;
13988 svn_wc__db_status_t base_status;
13990 SVN_ERR(svn_wc__db_base_get_info_internal(&base_status, NULL, NULL, NULL,
13991 NULL, NULL, NULL, NULL, NULL,
13992 NULL, NULL, NULL, NULL, NULL, NULL,
13993 wcroot, local_relpath,
13994 scratch_pool, scratch_pool));
13996 if (base_status == svn_wc__db_status_normal)
13997 return SVN_NO_ERROR;
13999 SVN_ERR_ASSERT(base_status == svn_wc__db_status_incomplete);
14001 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14002 STMT_UPDATE_NODE_BASE_PRESENCE));
14003 SVN_ERR(svn_sqlite__bindf(stmt, "ist", wcroot->wc_id, local_relpath,
14004 presence_map, svn_wc__db_status_normal));
14005 SVN_ERR(svn_sqlite__step_done(stmt));
14007 return SVN_NO_ERROR;
14011 svn_wc__db_temp_op_end_directory_update(svn_wc__db_t *db,
14012 const char *local_dir_abspath,
14013 apr_pool_t *scratch_pool)
14015 svn_wc__db_wcroot_t *wcroot;
14016 const char *local_relpath;
14018 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
14020 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14021 local_dir_abspath, scratch_pool, scratch_pool));
14022 VERIFY_USABLE_WCROOT(wcroot);
14024 SVN_WC__DB_WITH_TXN(
14025 end_directory_update(wcroot, local_relpath, scratch_pool),
14028 SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_empty,
14031 return SVN_NO_ERROR;
14035 /* The body of svn_wc__db_temp_op_start_directory_update().
14037 static svn_error_t *
14038 start_directory_update_txn(svn_wc__db_wcroot_t *wcroot,
14039 const char *local_relpath,
14040 const char *new_repos_relpath,
14041 svn_revnum_t new_rev,
14042 apr_pool_t *scratch_pool)
14044 svn_sqlite__stmt_t *stmt;
14046 /* Note: In the majority of calls, the repos_relpath is unchanged. */
14047 /* ### TODO: Maybe check if we can make repos_relpath NULL. */
14048 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14049 STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH));
14051 SVN_ERR(svn_sqlite__bindf(stmt, "istrs",
14054 presence_map, svn_wc__db_status_incomplete,
14056 new_repos_relpath));
14057 SVN_ERR(svn_sqlite__step_done(stmt));
14059 return SVN_NO_ERROR;
14064 svn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db,
14065 const char *local_abspath,
14066 const char *new_repos_relpath,
14067 svn_revnum_t new_rev,
14068 apr_pool_t *scratch_pool)
14070 svn_wc__db_wcroot_t *wcroot;
14071 const char *local_relpath;
14073 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14074 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_rev));
14075 SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
14077 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14078 local_abspath, scratch_pool, scratch_pool));
14079 VERIFY_USABLE_WCROOT(wcroot);
14081 SVN_WC__DB_WITH_TXN(
14082 start_directory_update_txn(wcroot, local_relpath,
14083 new_repos_relpath, new_rev, scratch_pool),
14086 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
14088 return SVN_NO_ERROR;
14092 /* The body of svn_wc__db_temp_op_make_copy(). This is
14093 used by the update editor when deleting a base node tree would be a
14094 tree-conflict because there are changes to subtrees. This function
14095 inserts a copy of the base node tree below any existing working
14096 subtrees. Given a tree:
14101 A/B normal - normal
14102 A/B/C normal - base-del normal
14103 A/F normal - normal
14104 A/F/G normal - normal
14105 A/F/H normal - base-deleted normal
14106 A/F/E normal - not-present
14110 This function adds layers to A and some of its descendants in an attempt
14111 to make the working copy look like as if it were a copy of the BASE nodes.
14116 A/B normal norm norm
14117 A/B/C normal norm base-del normal
14118 A/F normal norm norm
14119 A/F/G normal norm norm
14120 A/F/H normal norm not-pres
14121 A/F/E normal norm base-del
14123 A/X/Y incomplete incomplete
14125 static svn_error_t *
14126 make_copy_txn(svn_wc__db_wcroot_t *wcroot,
14127 const char *local_relpath,
14129 const svn_skel_t *conflicts,
14130 const svn_skel_t *work_items,
14131 apr_pool_t *scratch_pool)
14133 svn_sqlite__stmt_t *stmt;
14134 svn_boolean_t have_row;
14135 svn_boolean_t add_working_base_deleted = FALSE;
14136 svn_boolean_t remove_working = FALSE;
14137 const apr_array_header_t *children;
14138 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
14141 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14142 STMT_SELECT_LOWEST_WORKING_NODE));
14143 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
14144 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14148 svn_wc__db_status_t working_status;
14149 int working_op_depth;
14151 working_status = svn_sqlite__column_token(stmt, 1, presence_map);
14152 working_op_depth = svn_sqlite__column_int(stmt, 0);
14153 SVN_ERR(svn_sqlite__reset(stmt));
14155 SVN_ERR_ASSERT(working_status == svn_wc__db_status_normal
14156 || working_status == svn_wc__db_status_base_deleted
14157 || working_status == svn_wc__db_status_not_present
14158 || working_status == svn_wc__db_status_incomplete);
14160 /* Only change nodes in the layers where we are creating the copy.
14161 Deletes in higher layers will just apply to the copy */
14162 if (working_op_depth <= op_depth)
14164 add_working_base_deleted = TRUE;
14166 if (working_status == svn_wc__db_status_base_deleted)
14167 remove_working = TRUE;
14171 SVN_ERR(svn_sqlite__reset(stmt));
14173 if (remove_working)
14175 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14176 STMT_DELETE_LOWEST_WORKING_NODE));
14177 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14178 SVN_ERR(svn_sqlite__step_done(stmt));
14181 if (add_working_base_deleted)
14183 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14184 STMT_INSERT_DELETE_FROM_BASE));
14185 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14187 SVN_ERR(svn_sqlite__step_done(stmt));
14191 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14192 STMT_INSERT_WORKING_NODE_FROM_BASE_COPY));
14193 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14195 SVN_ERR(svn_sqlite__step_done(stmt));
14198 /* Get the BASE children, as WORKING children don't need modifications */
14199 SVN_ERR(gather_repo_children(&children, wcroot, local_relpath,
14200 0, scratch_pool, iterpool));
14202 for (i = 0; i < children->nelts; i++)
14204 const char *name = APR_ARRAY_IDX(children, i, const char *);
14205 const char *copy_relpath;
14207 svn_pool_clear(iterpool);
14209 copy_relpath = svn_relpath_join(local_relpath, name, iterpool);
14211 SVN_ERR(make_copy_txn(wcroot, copy_relpath, op_depth, NULL, NULL,
14215 SVN_ERR(flush_entries(wcroot, svn_dirent_join(wcroot->abspath, local_relpath,
14217 svn_depth_empty, iterpool));
14220 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
14221 conflicts, iterpool));
14223 SVN_ERR(add_work_items(wcroot->sdb, work_items, iterpool));
14225 svn_pool_destroy(iterpool);
14227 return SVN_NO_ERROR;
14232 svn_wc__db_op_make_copy(svn_wc__db_t *db,
14233 const char *local_abspath,
14234 const svn_skel_t *conflicts,
14235 const svn_skel_t *work_items,
14236 apr_pool_t *scratch_pool)
14238 svn_wc__db_wcroot_t *wcroot;
14239 const char *local_relpath;
14240 svn_sqlite__stmt_t *stmt;
14241 svn_boolean_t have_row;
14243 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14245 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14246 local_abspath, scratch_pool, scratch_pool));
14247 VERIFY_USABLE_WCROOT(wcroot);
14249 /* The update editor is supposed to call this function when there is
14250 no working node for LOCAL_ABSPATH. */
14251 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14252 STMT_SELECT_WORKING_NODE));
14253 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14254 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14255 SVN_ERR(svn_sqlite__reset(stmt));
14257 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
14258 _("Modification of '%s' already exists"),
14259 path_for_error_message(wcroot,
14263 /* We don't allow copies to contain server-excluded nodes;
14264 the update editor is going to have to bail out. */
14265 SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath, scratch_pool));
14267 SVN_WC__DB_WITH_TXN(
14268 make_copy_txn(wcroot, local_relpath,
14269 relpath_depth(local_relpath), conflicts, work_items,
14273 return SVN_NO_ERROR;
14277 svn_wc__db_info_below_working(svn_boolean_t *have_base,
14278 svn_boolean_t *have_work,
14279 svn_wc__db_status_t *status,
14281 const char *local_abspath,
14282 apr_pool_t *scratch_pool)
14284 svn_wc__db_wcroot_t *wcroot;
14285 const char *local_relpath;
14287 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14289 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14290 local_abspath, scratch_pool, scratch_pool));
14291 VERIFY_USABLE_WCROOT(wcroot);
14292 SVN_ERR(info_below_working(have_base, have_work, status,
14293 wcroot, local_relpath, -1, scratch_pool));
14295 return SVN_NO_ERROR;
14299 svn_wc__db_get_not_present_descendants(const apr_array_header_t **descendants,
14301 const char *local_abspath,
14302 apr_pool_t *result_pool,
14303 apr_pool_t *scratch_pool)
14305 svn_wc__db_wcroot_t *wcroot;
14306 const char *local_relpath;
14307 svn_sqlite__stmt_t *stmt;
14308 svn_boolean_t have_row;
14310 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14312 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14313 local_abspath, scratch_pool, scratch_pool));
14314 VERIFY_USABLE_WCROOT(wcroot);
14316 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14317 STMT_SELECT_NOT_PRESENT_DESCENDANTS));
14319 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
14322 relpath_depth(local_relpath)));
14324 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14328 apr_array_header_t *paths;
14330 paths = apr_array_make(result_pool, 4, sizeof(const char*));
14333 const char *found_relpath = svn_sqlite__column_text(stmt, 0, NULL);
14335 APR_ARRAY_PUSH(paths, const char *)
14336 = apr_pstrdup(result_pool, svn_relpath_skip_ancestor(
14337 local_relpath, found_relpath));
14339 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14342 *descendants = paths;
14345 *descendants = apr_array_make(result_pool, 0, sizeof(const char*));
14347 return svn_error_trace(svn_sqlite__reset(stmt));
14351 /* Like svn_wc__db_min_max_revisions(),
14352 * but accepts a WCROOT/LOCAL_RELPATH pair. */
14353 static svn_error_t *
14354 get_min_max_revisions(svn_revnum_t *min_revision,
14355 svn_revnum_t *max_revision,
14356 svn_wc__db_wcroot_t *wcroot,
14357 const char *local_relpath,
14358 svn_boolean_t committed,
14359 apr_pool_t *scratch_pool)
14361 svn_sqlite__stmt_t *stmt;
14362 svn_revnum_t min_rev, max_rev;
14364 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14365 STMT_SELECT_MIN_MAX_REVISIONS));
14366 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14367 SVN_ERR(svn_sqlite__step_row(stmt));
14371 min_rev = svn_sqlite__column_revnum(stmt, 2);
14372 max_rev = svn_sqlite__column_revnum(stmt, 3);
14376 min_rev = svn_sqlite__column_revnum(stmt, 0);
14377 max_rev = svn_sqlite__column_revnum(stmt, 1);
14380 /* The statement returns exactly one row. */
14381 SVN_ERR(svn_sqlite__reset(stmt));
14384 *min_revision = min_rev;
14386 *max_revision = max_rev;
14388 return SVN_NO_ERROR;
14393 svn_wc__db_min_max_revisions(svn_revnum_t *min_revision,
14394 svn_revnum_t *max_revision,
14396 const char *local_abspath,
14397 svn_boolean_t committed,
14398 apr_pool_t *scratch_pool)
14400 svn_wc__db_wcroot_t *wcroot;
14401 const char *local_relpath;
14403 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14405 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14407 scratch_pool, scratch_pool));
14408 VERIFY_USABLE_WCROOT(wcroot);
14410 return svn_error_trace(get_min_max_revisions(min_revision, max_revision,
14411 wcroot, local_relpath,
14412 committed, scratch_pool));
14416 /* Set *IS_SPARSE_CHECKOUT TRUE if LOCAL_RELPATH or any of the nodes
14417 * within LOCAL_RELPATH is sparse, FALSE otherwise. */
14418 static svn_error_t *
14419 is_sparse_checkout_internal(svn_boolean_t *is_sparse_checkout,
14420 svn_wc__db_wcroot_t *wcroot,
14421 const char *local_relpath,
14422 apr_pool_t *scratch_pool)
14424 svn_sqlite__stmt_t *stmt;
14425 svn_boolean_t have_row;
14427 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14428 STMT_HAS_SPARSE_NODES));
14429 SVN_ERR(svn_sqlite__bindf(stmt, "is",
14432 /* If this query returns a row, the working copy is sparse. */
14433 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14434 *is_sparse_checkout = have_row;
14435 SVN_ERR(svn_sqlite__reset(stmt));
14437 return SVN_NO_ERROR;
14441 /* Like svn_wc__db_has_switched_subtrees(),
14442 * but accepts a WCROOT/LOCAL_RELPATH pair. */
14443 static svn_error_t *
14444 has_switched_subtrees(svn_boolean_t *is_switched,
14445 svn_wc__db_wcroot_t *wcroot,
14446 const char *local_relpath,
14447 const char *trail_url,
14448 apr_pool_t *scratch_pool)
14450 svn_sqlite__stmt_t *stmt;
14451 svn_boolean_t have_row;
14452 apr_int64_t repos_id;
14453 const char *repos_relpath;
14455 /* Optional argument handling for caller */
14457 return SVN_NO_ERROR;
14459 *is_switched = FALSE;
14461 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
14462 &repos_relpath, &repos_id,
14463 NULL, NULL, NULL, NULL, NULL,
14464 NULL, NULL, NULL, NULL, NULL,
14465 wcroot, local_relpath,
14466 scratch_pool, scratch_pool));
14468 /* First do the cheap check where we only need info on the origin itself */
14469 if (trail_url != NULL)
14471 const char *repos_root_url;
14473 apr_size_t len1, len2;
14475 /* If the trailing part of the URL of the working copy directory
14476 does not match the given trailing URL then the whole working
14477 copy is switched. */
14479 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot->sdb,
14480 repos_id, scratch_pool));
14481 url = svn_path_url_add_component2(repos_root_url, repos_relpath,
14484 len1 = strlen(trail_url);
14485 len2 = strlen(url);
14486 if ((len1 > len2) || strcmp(url + len2 - len1, trail_url))
14488 *is_switched = TRUE;
14489 return SVN_NO_ERROR;
14493 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_HAS_SWITCHED));
14494 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath, repos_relpath));
14495 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14497 *is_switched = TRUE;
14498 SVN_ERR(svn_sqlite__reset(stmt));
14500 return SVN_NO_ERROR;
14505 svn_wc__db_has_switched_subtrees(svn_boolean_t *is_switched,
14507 const char *local_abspath,
14508 const char *trail_url,
14509 apr_pool_t *scratch_pool)
14511 svn_wc__db_wcroot_t *wcroot;
14512 const char *local_relpath;
14514 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14516 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14518 scratch_pool, scratch_pool));
14519 VERIFY_USABLE_WCROOT(wcroot);
14521 return svn_error_trace(has_switched_subtrees(is_switched, wcroot,
14522 local_relpath, trail_url,
14527 svn_wc__db_get_excluded_subtrees(apr_hash_t **excluded_subtrees,
14529 const char *local_abspath,
14530 apr_pool_t *result_pool,
14531 apr_pool_t *scratch_pool)
14533 svn_wc__db_wcroot_t *wcroot;
14534 const char *local_relpath;
14535 svn_sqlite__stmt_t *stmt;
14536 svn_boolean_t have_row;
14538 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14539 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14541 scratch_pool, scratch_pool));
14542 VERIFY_USABLE_WCROOT(wcroot);
14544 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14545 STMT_SELECT_ALL_EXCLUDED_DESCENDANTS));
14546 SVN_ERR(svn_sqlite__bindf(stmt, "is",
14549 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14552 *excluded_subtrees = apr_hash_make(result_pool);
14554 *excluded_subtrees = NULL;
14558 const char *abs_path =
14559 svn_dirent_join(wcroot->abspath,
14560 svn_sqlite__column_text(stmt, 0, NULL),
14562 svn_hash_sets(*excluded_subtrees, abs_path, abs_path);
14563 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14566 SVN_ERR(svn_sqlite__reset(stmt));
14567 return SVN_NO_ERROR;
14570 /* Like svn_wc__db_has_local_mods(),
14571 * but accepts a WCROOT/LOCAL_RELPATH pair.
14572 * ### This needs a DB as well as a WCROOT/RELPATH pair... */
14573 static svn_error_t *
14574 has_local_mods(svn_boolean_t *is_modified,
14575 svn_wc__db_wcroot_t *wcroot,
14576 const char *local_relpath,
14578 svn_cancel_func_t cancel_func,
14579 void *cancel_baton,
14580 apr_pool_t *scratch_pool)
14582 svn_sqlite__stmt_t *stmt;
14584 /* Check for additions or deletions. */
14585 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14586 STMT_SUBTREE_HAS_TREE_MODIFICATIONS));
14587 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14588 /* If this query returns a row, the working copy is modified. */
14589 SVN_ERR(svn_sqlite__step(is_modified, stmt));
14590 SVN_ERR(svn_sqlite__reset(stmt));
14593 SVN_ERR(cancel_func(cancel_baton));
14595 if (! *is_modified)
14597 /* Check for property modifications. */
14598 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14599 STMT_SUBTREE_HAS_PROP_MODIFICATIONS));
14600 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14601 /* If this query returns a row, the working copy is modified. */
14602 SVN_ERR(svn_sqlite__step(is_modified, stmt));
14603 SVN_ERR(svn_sqlite__reset(stmt));
14606 SVN_ERR(cancel_func(cancel_baton));
14609 if (! *is_modified)
14611 apr_pool_t *iterpool = NULL;
14612 svn_boolean_t have_row;
14614 /* Check for text modifications. */
14615 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14616 STMT_SELECT_BASE_FILES_RECURSIVE));
14617 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14618 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14620 iterpool = svn_pool_create(scratch_pool);
14623 const char *node_abspath;
14624 svn_filesize_t recorded_size;
14625 apr_time_t recorded_time;
14626 svn_boolean_t skip_check = FALSE;
14631 err = cancel_func(cancel_baton);
14633 return svn_error_trace(svn_error_compose_create(
14635 svn_sqlite__reset(stmt)));
14638 svn_pool_clear(iterpool);
14640 node_abspath = svn_dirent_join(wcroot->abspath,
14641 svn_sqlite__column_text(stmt, 0,
14645 recorded_size = get_recorded_size(stmt, 1);
14646 recorded_time = svn_sqlite__column_int64(stmt, 2);
14648 if (recorded_size != SVN_INVALID_FILESIZE
14649 && recorded_time != 0)
14651 const svn_io_dirent2_t *dirent;
14653 err = svn_io_stat_dirent2(&dirent, node_abspath, FALSE, TRUE,
14654 iterpool, iterpool);
14656 return svn_error_trace(svn_error_compose_create(
14658 svn_sqlite__reset(stmt)));
14660 if (dirent->kind != svn_node_file)
14662 *is_modified = TRUE; /* Missing or obstruction */
14665 else if (dirent->filesize == recorded_size
14666 && dirent->mtime == recorded_time)
14668 /* The file is not modified */
14675 err = svn_wc__internal_file_modified_p(is_modified,
14680 return svn_error_trace(svn_error_compose_create(
14682 svn_sqlite__reset(stmt)));
14688 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14691 svn_pool_destroy(iterpool);
14693 SVN_ERR(svn_sqlite__reset(stmt));
14696 return SVN_NO_ERROR;
14701 svn_wc__db_has_local_mods(svn_boolean_t *is_modified,
14703 const char *local_abspath,
14704 svn_cancel_func_t cancel_func,
14705 void *cancel_baton,
14706 apr_pool_t *scratch_pool)
14708 svn_wc__db_wcroot_t *wcroot;
14709 const char *local_relpath;
14711 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14713 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14715 scratch_pool, scratch_pool));
14716 VERIFY_USABLE_WCROOT(wcroot);
14718 return svn_error_trace(has_local_mods(is_modified, wcroot, local_relpath,
14719 db, cancel_func, cancel_baton,
14724 /* The body of svn_wc__db_revision_status().
14726 static svn_error_t *
14727 revision_status_txn(svn_revnum_t *min_revision,
14728 svn_revnum_t *max_revision,
14729 svn_boolean_t *is_sparse_checkout,
14730 svn_boolean_t *is_modified,
14731 svn_boolean_t *is_switched,
14732 svn_wc__db_wcroot_t *wcroot,
14733 const char *local_relpath,
14735 const char *trail_url,
14736 svn_boolean_t committed,
14737 svn_cancel_func_t cancel_func,
14738 void *cancel_baton,
14739 apr_pool_t *scratch_pool)
14742 svn_boolean_t exists;
14744 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
14748 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14749 _("The node '%s' was not found."),
14750 path_for_error_message(wcroot, local_relpath,
14754 /* Determine mixed-revisionness. */
14755 SVN_ERR(get_min_max_revisions(min_revision, max_revision, wcroot,
14756 local_relpath, committed, scratch_pool));
14759 SVN_ERR(cancel_func(cancel_baton));
14761 /* Determine sparseness. */
14762 SVN_ERR(is_sparse_checkout_internal(is_sparse_checkout, wcroot,
14763 local_relpath, scratch_pool));
14766 SVN_ERR(cancel_func(cancel_baton));
14768 /* Check for switched nodes. */
14770 err = has_switched_subtrees(is_switched, wcroot, local_relpath,
14771 trail_url, scratch_pool);
14775 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
14776 return svn_error_trace(err);
14778 svn_error_clear(err); /* No Base node, but no fatal error */
14779 *is_switched = FALSE;
14784 SVN_ERR(cancel_func(cancel_baton));
14786 /* Check for local mods. */
14787 SVN_ERR(has_local_mods(is_modified, wcroot, local_relpath, db,
14788 cancel_func, cancel_baton, scratch_pool));
14790 return SVN_NO_ERROR;
14795 svn_wc__db_revision_status(svn_revnum_t *min_revision,
14796 svn_revnum_t *max_revision,
14797 svn_boolean_t *is_sparse_checkout,
14798 svn_boolean_t *is_modified,
14799 svn_boolean_t *is_switched,
14801 const char *local_abspath,
14802 const char *trail_url,
14803 svn_boolean_t committed,
14804 svn_cancel_func_t cancel_func,
14805 void *cancel_baton,
14806 apr_pool_t *scratch_pool)
14808 svn_wc__db_wcroot_t *wcroot;
14809 const char *local_relpath;
14811 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14813 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14815 scratch_pool, scratch_pool));
14816 VERIFY_USABLE_WCROOT(wcroot);
14818 SVN_WC__DB_WITH_TXN(
14819 revision_status_txn(min_revision, max_revision,
14820 is_sparse_checkout, is_modified, is_switched,
14821 wcroot, local_relpath, db,
14822 trail_url, committed, cancel_func, cancel_baton,
14825 return SVN_NO_ERROR;
14830 svn_wc__db_base_get_lock_tokens_recursive(apr_hash_t **lock_tokens,
14832 const char *local_abspath,
14833 apr_pool_t *result_pool,
14834 apr_pool_t *scratch_pool)
14836 svn_wc__db_wcroot_t *wcroot;
14837 const char *local_relpath;
14838 svn_sqlite__stmt_t *stmt;
14839 svn_boolean_t have_row;
14840 apr_int64_t last_repos_id = INVALID_REPOS_ID;
14841 const char *last_repos_root_url = NULL;
14843 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14845 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14847 scratch_pool, scratch_pool));
14848 VERIFY_USABLE_WCROOT(wcroot);
14850 *lock_tokens = apr_hash_make(result_pool);
14852 /* Fetch all the lock tokens in and under LOCAL_RELPATH. */
14853 SVN_ERR(svn_sqlite__get_statement(
14854 &stmt, wcroot->sdb,
14855 STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE));
14857 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14858 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14861 apr_int64_t child_repos_id = svn_sqlite__column_int64(stmt, 0);
14862 const char *child_relpath = svn_sqlite__column_text(stmt, 1, NULL);
14863 const char *lock_token = svn_sqlite__column_text(stmt, 2, result_pool);
14865 if (child_repos_id != last_repos_id)
14867 svn_error_t *err = svn_wc__db_fetch_repos_info(&last_repos_root_url,
14874 return svn_error_trace(
14875 svn_error_compose_create(err,
14876 svn_sqlite__reset(stmt)));
14879 last_repos_id = child_repos_id;
14882 SVN_ERR_ASSERT(last_repos_root_url != NULL);
14883 svn_hash_sets(*lock_tokens,
14884 svn_path_url_add_component2(last_repos_root_url,
14885 child_relpath, result_pool),
14888 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14890 return svn_sqlite__reset(stmt);
14894 /* If EXPRESSION is false, cause the caller to return an SVN_ERR_WC_CORRUPT
14895 * error, showing EXPRESSION and the caller's LOCAL_RELPATH in the message. */
14896 #define VERIFY(expression) \
14898 if (! (expression)) \
14899 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, \
14900 _("database inconsistency at local_relpath='%s' verifying " \
14901 "expression '%s'"), local_relpath, #expression); \
14905 /* Verify consistency of the metadata concerning WCROOT. This is intended
14906 * for use only during testing and debugging, so is not intended to be
14909 * This code is a complement to any verification that we can do in SQLite
14910 * triggers. See, for example, 'wc-checks.sql'.
14912 * Some more verification steps we might want to add are:
14914 * * on every ACTUAL row (except root): a NODES row exists at its parent path
14915 * * the op-depth root must always exist and every intermediate too
14917 static svn_error_t *
14918 verify_wcroot(svn_wc__db_wcroot_t *wcroot,
14919 apr_pool_t *scratch_pool)
14921 svn_sqlite__stmt_t *stmt;
14922 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
14924 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14925 STMT_SELECT_ALL_NODES));
14926 SVN_ERR(svn_sqlite__bindf(stmt, "i", wcroot->wc_id));
14929 svn_boolean_t have_row;
14930 const char *local_relpath, *parent_relpath;
14933 svn_pool_clear(iterpool);
14935 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14939 op_depth = svn_sqlite__column_int(stmt, 0);
14940 local_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
14941 parent_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
14943 /* Verify parent_relpath is the parent path of local_relpath */
14944 VERIFY((parent_relpath == NULL)
14945 ? (local_relpath[0] == '\0')
14946 : (strcmp(svn_relpath_dirname(local_relpath, iterpool),
14947 parent_relpath) == 0));
14949 /* Verify op_depth <= the tree depth of local_relpath */
14950 VERIFY(op_depth <= relpath_depth(local_relpath));
14952 /* Verify parent_relpath refers to a row that exists */
14953 /* TODO: Verify there is a suitable parent row - e.g. has op_depth <=
14954 * the child's and a suitable presence */
14955 if (parent_relpath && svn_sqlite__column_is_null(stmt, 3))
14957 svn_sqlite__stmt_t *stmt2;
14958 svn_boolean_t have_a_parent_row;
14960 SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
14961 STMT_SELECT_NODE_INFO));
14962 SVN_ERR(svn_sqlite__bindf(stmt2, "is", wcroot->wc_id,
14964 SVN_ERR(svn_sqlite__step(&have_a_parent_row, stmt2));
14965 VERIFY(have_a_parent_row);
14966 SVN_ERR(svn_sqlite__reset(stmt2));
14969 svn_pool_destroy(iterpool);
14971 return svn_error_trace(svn_sqlite__reset(stmt));
14975 svn_wc__db_verify(svn_wc__db_t *db,
14976 const char *wri_abspath,
14977 apr_pool_t *scratch_pool)
14979 svn_wc__db_wcroot_t *wcroot;
14980 const char *local_relpath;
14982 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14984 scratch_pool, scratch_pool));
14985 VERIFY_USABLE_WCROOT(wcroot);
14987 SVN_ERR(verify_wcroot(wcroot, scratch_pool));
14988 return SVN_NO_ERROR;
14992 svn_wc__db_bump_format(int *result_format,
14993 svn_boolean_t *bumped_format,
14995 const char *wcroot_abspath,
14996 apr_pool_t *scratch_pool)
14998 svn_sqlite__db_t *sdb;
15003 *bumped_format = FALSE;
15005 /* Do not scan upwards for a working copy root here to prevent accidental
15006 * upgrades of any working copies the WCROOT might be nested in.
15007 * Just try to open a DB at the specified path instead. */
15008 err = svn_wc__db_util_open_db(&sdb, wcroot_abspath, SDB_FILE,
15009 svn_sqlite__mode_readwrite,
15010 TRUE, /* exclusive */
15011 NULL, /* my statements */
15012 scratch_pool, scratch_pool);
15016 apr_hash_t *entries;
15018 /* Could not open an sdb. Check for an entries file instead. */
15019 err2 = svn_wc__read_entries_old(&entries, wcroot_abspath,
15020 scratch_pool, scratch_pool);
15021 if (err2 || apr_hash_count(entries) == 0)
15022 return svn_error_createf(SVN_ERR_WC_INVALID_OP_ON_CWD,
15023 svn_error_compose_create(err, err2),
15024 _("Can't upgrade '%s' as it is not a working copy root"),
15025 svn_dirent_local_style(wcroot_abspath, scratch_pool));
15027 /* An entries file was found. This is a pre-wc-ng working copy
15028 * so suggest an upgrade. */
15029 return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, err,
15030 _("Working copy '%s' is too old and must be upgraded to "
15031 "at least format %d, as created by Subversion %s"),
15032 svn_dirent_local_style(wcroot_abspath, scratch_pool),
15033 SVN_WC__WC_NG_VERSION,
15034 svn_wc__version_string_from_format(SVN_WC__WC_NG_VERSION));
15037 SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool));
15038 err = svn_wc__upgrade_sdb(result_format, wcroot_abspath,
15039 sdb, format, scratch_pool);
15041 if (err == SVN_NO_ERROR && bumped_format)
15042 *bumped_format = (*result_format > format);
15044 /* Make sure we return a different error than expected for upgrades from
15046 if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
15047 err = svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, err,
15048 _("Working copy upgrade failed"));
15050 err = svn_error_compose_create(err, svn_sqlite__close(sdb));
15052 return svn_error_trace(err);
15056 svn_wc__db_vacuum(svn_wc__db_t *db,
15057 const char *local_abspath,
15058 apr_pool_t *scratch_pool)
15060 svn_wc__db_wcroot_t *wcroot;
15061 const char *local_relpath;
15063 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15065 scratch_pool, scratch_pool));
15066 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_VACUUM));
15068 return SVN_NO_ERROR;