1 /*
2 * wc_db.c : manipulating the administrative database
3 *
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
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
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
20 * under the License.
21 * ====================================================================
22 */
23
24 #define SVN_WC__I_AM_WC_DB
25
26 #include <assert.h>
27 #include <apr_pools.h>
28 #include <apr_hash.h>
29
30 #include "svn_private_config.h"
31 #include "svn_types.h"
32 #include "svn_error.h"
33 #include "svn_dirent_uri.h"
34 #include "svn_path.h"
35 #include "svn_hash.h"
36 #include "svn_sorts.h"
37 #include "svn_wc.h"
38 #include "svn_checksum.h"
39 #include "svn_pools.h"
40
41 #include "wc.h"
42 #include "wc_db.h"
43 #include "adm_files.h"
44 #include "wc-queries.h"
45 #include "entries.h"
46 #include "lock.h"
47 #include "conflicts.h"
48 #include "wc_db_private.h"
49 #include "workqueue.h"
50 #include "token-map.h"
51
52 #include "private/svn_sorts_private.h"
53 #include "private/svn_sqlite.h"
54 #include "private/svn_skel.h"
55 #include "private/svn_wc_private.h"
56 #include "private/svn_token.h"
57
58
59 #define NOT_IMPLEMENTED() SVN__NOT_IMPLEMENTED()
60
61
62 /*
63 * Some filename constants.
64 */
65 #define SDB_FILE "wc.db"
66
67 #define WCROOT_TEMPDIR_RELPATH "tmp"
68
69
70 /*
71 * PARAMETER ASSERTIONS
72 *
73 * Every (semi-)public entrypoint in this file has a set of assertions on
74 * the parameters passed into the function. Since this is a brand new API,
75 * we want to make sure that everybody calls it properly. The original WC
76 * code had years to catch stray bugs, but we do not have that luxury in
77 * the wc-nb rewrite. Any extra assurances that we can find will be
78 * welcome. The asserts will ensure we have no doubt about the values
79 * passed into the function.
80 *
81 * Some parameters are *not* specifically asserted. Typically, these are
82 * params that will be used immediately, so something like a NULL value
83 * will be obvious.
84 *
85 * ### near 1.7 release, it would be a Good Thing to review the assertions
86 * ### and decide if any can be removed or switched to assert() in order
87 * ### to remove their runtime cost in the production release.
88 *
89 *
90 * DATABASE OPERATIONS
91 *
92 * Each function should leave the database in a consistent state. If it
93 * does *not*, then the implication is some other function needs to be
94 * called to restore consistency. Subtle requirements like that are hard
95 * to maintain over a long period of time, so this API will not allow it.
96 *
97 *
98 * STANDARD VARIABLE NAMES
99 *
100 * db working copy database (this module)
101 * sdb SQLite database (not to be confused with 'db')
102 * wc_id a WCROOT id associated with a node
103 */
104
105 #define INVALID_REPOS_ID ((apr_int64_t) -1)
106 #define UNKNOWN_WC_ID ((apr_int64_t) -1)
107 #define FORMAT_FROM_SDB (-1)
108
109 /* Check if column number I, a property-skel column, contains a non-empty
110 set of properties. The empty set of properties is stored as "()", so we
111 have properties if the size of the column is larger than 2. */
112 #define SQLITE_PROPERTIES_AVAILABLE(stmt, i) \
113 (svn_sqlite__column_bytes(stmt, i) > 2)
114
115 int
svn_wc__db_op_depth_for_upgrade(const char * local_relpath)116 svn_wc__db_op_depth_for_upgrade(const char *local_relpath)
117 {
118 return relpath_depth(local_relpath);
119 }
120
121
122 /* Representation of a new base row for the NODES table */
123 typedef struct insert_base_baton_t {
124 /* common to all insertions into BASE */
125 svn_wc__db_status_t status;
126 svn_node_kind_t kind;
127 apr_int64_t repos_id;
128 const char *repos_relpath;
129 svn_revnum_t revision;
130
131 /* Only used when repos_id == INVALID_REPOS_ID */
132 const char *repos_root_url;
133 const char *repos_uuid;
134
135 /* common to all "normal" presence insertions */
136 const apr_hash_t *props;
137 svn_revnum_t changed_rev;
138 apr_time_t changed_date;
139 const char *changed_author;
140 const apr_hash_t *dav_cache;
141
142 /* for inserting directories */
143 const apr_array_header_t *children;
144 svn_depth_t depth;
145
146 /* for inserting files */
147 const svn_checksum_t *checksum;
148
149 /* for inserting symlinks */
150 const char *target;
151
152 svn_boolean_t file_external;
153
154 /* may need to insert/update ACTUAL to record a conflict */
155 const svn_skel_t *conflict;
156
157 /* may need to insert/update ACTUAL to record new properties */
158 svn_boolean_t update_actual_props;
159 const apr_hash_t *new_actual_props;
160
161 /* A depth-first ordered array of svn_prop_inherited_item_t *
162 structures representing the properties inherited by the base
163 node. */
164 apr_array_header_t *iprops;
165
166 /* maybe we should copy information from a previous record? */
167 svn_boolean_t keep_recorded_info;
168
169 /* insert a base-deleted working node as well as a base node */
170 svn_boolean_t insert_base_deleted;
171
172 /* delete the current working nodes above BASE */
173 svn_boolean_t delete_working;
174
175 /* may have work items to queue in this transaction */
176 const svn_skel_t *work_items;
177
178 } insert_base_baton_t;
179
180
181 /* Representation of a new working row for the NODES table */
182 typedef struct insert_working_baton_t {
183 /* common to all insertions into WORKING (including NODE_DATA) */
184 svn_wc__db_status_t presence;
185 svn_node_kind_t kind;
186 int op_depth;
187
188 /* common to all "normal" presence insertions */
189 const apr_hash_t *props;
190 svn_revnum_t changed_rev;
191 apr_time_t changed_date;
192 const char *changed_author;
193 apr_int64_t original_repos_id;
194 const char *original_repos_relpath;
195 svn_revnum_t original_revnum;
196 svn_boolean_t moved_here;
197
198 /* for inserting directories */
199 const apr_array_header_t *children;
200 svn_depth_t depth;
201
202 /* for inserting (copied/moved-here) files */
203 const svn_checksum_t *checksum;
204
205 /* for inserting symlinks */
206 const char *target;
207
208 svn_boolean_t update_actual_props;
209 const apr_hash_t *new_actual_props;
210
211 /* may have work items to queue in this transaction */
212 const svn_skel_t *work_items;
213
214 /* may have conflict to install in this transaction */
215 const svn_skel_t *conflict;
216
217 /* If the value is > 0 and < op_depth, also insert a not-present
218 at op-depth NOT_PRESENT_OP_DEPTH, based on this same information */
219 int not_present_op_depth;
220
221 } insert_working_baton_t;
222
223 /* Representation of a new row for the EXTERNALS table */
224 typedef struct insert_external_baton_t {
225 /* common to all insertions into EXTERNALS */
226 svn_node_kind_t kind;
227 svn_wc__db_status_t presence;
228
229 /* The repository of the external */
230 apr_int64_t repos_id;
231 /* for file and symlink externals */
232 const char *repos_relpath;
233 svn_revnum_t revision;
234
235 /* Only used when repos_id == INVALID_REPOS_ID */
236 const char *repos_root_url;
237 const char *repos_uuid;
238
239 /* for file and symlink externals */
240 const apr_hash_t *props;
241 apr_array_header_t *iprops;
242 svn_revnum_t changed_rev;
243 apr_time_t changed_date;
244 const char *changed_author;
245 const apr_hash_t *dav_cache;
246
247 /* for inserting files */
248 const svn_checksum_t *checksum;
249
250 /* for inserting symlinks */
251 const char *target;
252
253 const char *record_ancestor_relpath;
254 const char *recorded_repos_relpath;
255 svn_revnum_t recorded_peg_revision;
256 svn_revnum_t recorded_revision;
257
258 /* may need to insert/update ACTUAL to record a conflict */
259 const svn_skel_t *conflict;
260
261 /* may need to insert/update ACTUAL to record new properties */
262 svn_boolean_t update_actual_props;
263 const apr_hash_t *new_actual_props;
264
265 /* maybe we should copy information from a previous record? */
266 svn_boolean_t keep_recorded_info;
267
268 /* may have work items to queue in this transaction */
269 const svn_skel_t *work_items;
270
271 } insert_external_baton_t;
272
273
274 /* Forward declarations */
275 static svn_error_t *
276 add_work_items(svn_sqlite__db_t *sdb,
277 const svn_skel_t *skel,
278 apr_pool_t *scratch_pool);
279
280 static svn_error_t *
281 set_actual_props(svn_wc__db_wcroot_t *wcroot,
282 const char *local_relpath,
283 apr_hash_t *props,
284 apr_pool_t *scratch_pool);
285
286 static svn_error_t *
287 insert_incomplete_children(svn_sqlite__db_t *sdb,
288 apr_int64_t wc_id,
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,
294 int op_depth,
295 apr_pool_t *scratch_pool);
296
297 static svn_error_t *
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);
304
305 static svn_error_t *
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,
314 svn_depth_t *depth,
315 const svn_checksum_t **checksum,
316 const char **target,
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);
335
336 static svn_error_t *
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);
351
352 static svn_error_t *
353 convert_to_working_status(svn_wc__db_status_t *working_status,
354 svn_wc__db_status_t status);
355
356 static svn_error_t *
357 db_is_switched(svn_boolean_t *is_switched,
358 svn_node_kind_t *kind,
359 svn_wc__db_wcroot_t *wcroot,
360 const char *local_relpath,
361 apr_pool_t *scratch_pool);
362
363
364 /* Return the absolute path, in local path style, of LOCAL_RELPATH
365 in WCROOT. */
366 static const char *
path_for_error_message(const svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool)367 path_for_error_message(const svn_wc__db_wcroot_t *wcroot,
368 const char *local_relpath,
369 apr_pool_t *result_pool)
370 {
371 const char *local_abspath
372 = svn_dirent_join(wcroot->abspath, local_relpath, result_pool);
373
374 return svn_dirent_local_style(local_abspath, result_pool);
375 }
376
377
378 /* Return a file size from column SLOT of the SQLITE statement STMT, or
379 SVN_INVALID_FILESIZE if the column value is NULL. */
380 static svn_filesize_t
get_recorded_size(svn_sqlite__stmt_t * stmt,int slot)381 get_recorded_size(svn_sqlite__stmt_t *stmt, int slot)
382 {
383 if (svn_sqlite__column_is_null(stmt, slot))
384 return SVN_INVALID_FILESIZE;
385 return svn_sqlite__column_int64(stmt, slot);
386 }
387
388
389 /* Return a lock info structure constructed from the given columns of the
390 SQLITE statement STMT, or return NULL if the token column value is null. */
391 static svn_wc__db_lock_t *
lock_from_columns(svn_sqlite__stmt_t * stmt,int col_token,int col_owner,int col_comment,int col_date,apr_pool_t * result_pool)392 lock_from_columns(svn_sqlite__stmt_t *stmt,
393 int col_token,
394 int col_owner,
395 int col_comment,
396 int col_date,
397 apr_pool_t *result_pool)
398 {
399 svn_wc__db_lock_t *lock;
400
401 if (svn_sqlite__column_is_null(stmt, col_token))
402 {
403 lock = NULL;
404 }
405 else
406 {
407 lock = apr_pcalloc(result_pool, sizeof(svn_wc__db_lock_t));
408 lock->token = svn_sqlite__column_text(stmt, col_token, result_pool);
409 lock->owner = svn_sqlite__column_text(stmt, col_owner, result_pool);
410 lock->comment = svn_sqlite__column_text(stmt, col_comment, result_pool);
411 lock->date = svn_sqlite__column_int64(stmt, col_date);
412 }
413 return lock;
414 }
415
416
417 svn_error_t *
svn_wc__db_fetch_repos_info(const char ** repos_root_url,const char ** repos_uuid,svn_wc__db_wcroot_t * wcroot,apr_int64_t repos_id,apr_pool_t * result_pool)418 svn_wc__db_fetch_repos_info(const char **repos_root_url,
419 const char **repos_uuid,
420 svn_wc__db_wcroot_t *wcroot,
421 apr_int64_t repos_id,
422 apr_pool_t *result_pool)
423 {
424 svn_sqlite__stmt_t *stmt;
425 svn_boolean_t have_row;
426
427 if (!repos_root_url && !repos_uuid)
428 return SVN_NO_ERROR;
429
430 if (repos_id == INVALID_REPOS_ID)
431 {
432 if (repos_root_url)
433 *repos_root_url = NULL;
434 if (repos_uuid)
435 *repos_uuid = NULL;
436 return SVN_NO_ERROR;
437 }
438
439 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
440 STMT_SELECT_REPOSITORY_BY_ID));
441 SVN_ERR(svn_sqlite__bindf(stmt, "i", repos_id));
442 SVN_ERR(svn_sqlite__step(&have_row, stmt));
443 if (!have_row)
444 return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
445 _("No REPOSITORY table entry for id '%ld'"),
446 (long int)repos_id);
447
448 if (repos_root_url)
449 *repos_root_url = svn_sqlite__column_text(stmt, 0, result_pool);
450 if (repos_uuid)
451 *repos_uuid = svn_sqlite__column_text(stmt, 1, result_pool);
452
453 return svn_error_trace(svn_sqlite__reset(stmt));
454 }
455
456 /* Set *REPOS_ID, *REVISION and *REPOS_RELPATH from the given columns of the
457 SQLITE statement STMT, or to NULL/SVN_INVALID_REVNUM if the respective
458 column value is null. Any of the output parameters may be NULL if not
459 required. */
460 static void
repos_location_from_columns(apr_int64_t * repos_id,svn_revnum_t * revision,const char ** repos_relpath,svn_sqlite__stmt_t * stmt,int col_repos_id,int col_revision,int col_repos_relpath,apr_pool_t * result_pool)461 repos_location_from_columns(apr_int64_t *repos_id,
462 svn_revnum_t *revision,
463 const char **repos_relpath,
464 svn_sqlite__stmt_t *stmt,
465 int col_repos_id,
466 int col_revision,
467 int col_repos_relpath,
468 apr_pool_t *result_pool)
469 {
470 if (repos_id)
471 {
472 /* Fetch repository information via REPOS_ID. */
473 if (svn_sqlite__column_is_null(stmt, col_repos_id))
474 *repos_id = INVALID_REPOS_ID;
475 else
476 *repos_id = svn_sqlite__column_int64(stmt, col_repos_id);
477 }
478 if (revision)
479 {
480 *revision = svn_sqlite__column_revnum(stmt, col_revision);
481 }
482 if (repos_relpath)
483 {
484 *repos_relpath = svn_sqlite__column_text(stmt, col_repos_relpath,
485 result_pool);
486 }
487 }
488
489 /* For a given REPOS_ROOT_URL/REPOS_UUID pair, return the existing REPOS_ID
490 value. If one does not exist, then create a new one. */
491 static svn_error_t *
create_repos_id(apr_int64_t * repos_id,const char * repos_root_url,const char * repos_uuid,svn_sqlite__db_t * sdb,apr_pool_t * scratch_pool)492 create_repos_id(apr_int64_t *repos_id,
493 const char *repos_root_url,
494 const char *repos_uuid,
495 svn_sqlite__db_t *sdb,
496 apr_pool_t *scratch_pool)
497 {
498 svn_sqlite__stmt_t *get_stmt;
499 svn_sqlite__stmt_t *insert_stmt;
500 svn_boolean_t have_row;
501
502 SVN_ERR(svn_sqlite__get_statement(&get_stmt, sdb, STMT_SELECT_REPOSITORY));
503 SVN_ERR(svn_sqlite__bindf(get_stmt, "s", repos_root_url));
504 SVN_ERR(svn_sqlite__step(&have_row, get_stmt));
505
506 if (have_row)
507 {
508 *repos_id = svn_sqlite__column_int64(get_stmt, 0);
509 return svn_error_trace(svn_sqlite__reset(get_stmt));
510 }
511 SVN_ERR(svn_sqlite__reset(get_stmt));
512
513 /* NOTE: strictly speaking, there is a race condition between the
514 above query and the insertion below. We're simply going to ignore
515 that, as it means two processes are *modifying* the working copy
516 at the same time, *and* new repositores are becoming visible.
517 This is rare enough, let alone the miniscule chance of hitting
518 this race condition. Further, simply failing out will leave the
519 database in a consistent state, and the user can just re-run the
520 failed operation. */
521
522 SVN_ERR(svn_sqlite__get_statement(&insert_stmt, sdb,
523 STMT_INSERT_REPOSITORY));
524 SVN_ERR(svn_sqlite__bindf(insert_stmt, "ss", repos_root_url, repos_uuid));
525 return svn_error_trace(svn_sqlite__insert(repos_id, insert_stmt));
526 }
527
528
529 /* Initialize the baton with appropriate "blank" values. This allows the
530 insertion function to leave certain columns null. */
531 static void
blank_ibb(insert_base_baton_t * pibb)532 blank_ibb(insert_base_baton_t *pibb)
533 {
534 memset(pibb, 0, sizeof(*pibb));
535 pibb->revision = SVN_INVALID_REVNUM;
536 pibb->changed_rev = SVN_INVALID_REVNUM;
537 pibb->depth = svn_depth_infinity;
538 pibb->repos_id = INVALID_REPOS_ID;
539 }
540
541
542 /* Extend any delete of the parent of LOCAL_RELPATH to LOCAL_RELPATH.
543
544 ### What about KIND and OP_DEPTH? KIND ought to be redundant; I'm
545 discussing on dev@ whether we can let that be null for presence
546 == base-deleted. OP_DEPTH is the op-depth of what, and why?
547 It is used to select the lowest working node higher than OP_DEPTH,
548 so, in terms of the API, OP_DEPTH means ...?
549
550 Given a wc:
551
552 0 1 2 3 4
553 normal
554 A normal
555 A/B normal normal
556 A/B/C not-pres normal
557 A/B/C/D normal
558
559 That is checkout, delete A/B, copy a replacement A/B, delete copied
560 child A/B/C, add replacement A/B/C, add A/B/C/D.
561
562 Now an update that adds base nodes for A/B/C, A/B/C/D and A/B/C/D/E
563 must extend the A/B deletion:
564
565 0 1 2 3 4
566 normal
567 A normal
568 A/B normal normal
569 A/B/C normal not-pres normal
570 A/B/C/D normal base-del normal
571 A/B/C/D/E normal base-del
572
573 When adding a node if the parent has a higher working node then the
574 parent node is deleted (or replaced) and the delete must be extended
575 to cover new node.
576
577 In the example above A/B/C/D and A/B/C/D/E are the nodes that get
578 the extended delete, A/B/C is already deleted.
579
580 If ADDED_DELETE is not NULL, set *ADDED_DELETE to TRUE if a new delete
581 was recorded, otherwise to FALSE.
582 */
583 static svn_error_t *
db_extend_parent_delete(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_node_kind_t kind,int op_depth,apr_pool_t * scratch_pool)584 db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot,
585 const char *local_relpath,
586 svn_node_kind_t kind,
587 int op_depth,
588 apr_pool_t *scratch_pool)
589 {
590 svn_boolean_t have_row;
591 svn_sqlite__stmt_t *stmt;
592 int parent_op_depth;
593 const char *parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
594
595 SVN_ERR_ASSERT(local_relpath[0]);
596
597 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
598 STMT_SELECT_LOWEST_WORKING_NODE));
599 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, parent_relpath,
600 op_depth));
601 SVN_ERR(svn_sqlite__step(&have_row, stmt));
602 if (have_row)
603 parent_op_depth = svn_sqlite__column_int(stmt, 0);
604 SVN_ERR(svn_sqlite__reset(stmt));
605 if (have_row)
606 {
607 int existing_op_depth;
608
609 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
610 op_depth));
611 SVN_ERR(svn_sqlite__step(&have_row, stmt));
612 if (have_row)
613 existing_op_depth = svn_sqlite__column_int(stmt, 0);
614 SVN_ERR(svn_sqlite__reset(stmt));
615 if (!have_row || parent_op_depth < existing_op_depth)
616 {
617 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
618 STMT_INSTALL_WORKING_NODE_FOR_DELETE));
619 SVN_ERR(svn_sqlite__bindf(stmt, "isdst", wcroot->wc_id,
620 local_relpath, parent_op_depth,
621 parent_relpath, kind_map, kind));
622 SVN_ERR(svn_sqlite__update(NULL, stmt));
623 }
624 }
625
626 return SVN_NO_ERROR;
627 }
628
629
630 /* This is the reverse of db_extend_parent_delete.
631
632 When removing a node if the parent has a higher working node then
633 the parent node and this node are both deleted or replaced and any
634 delete over this node must be removed.
635
636 This function (like most wcroot functions) assumes that its caller
637 only uses this function within an sqlite transaction if atomic
638 behavior is needed.
639 */
640 static svn_error_t *
db_retract_parent_delete(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int op_depth,apr_pool_t * scratch_pool)641 db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot,
642 const char *local_relpath,
643 int op_depth,
644 apr_pool_t *scratch_pool)
645 {
646 svn_sqlite__stmt_t *stmt;
647 svn_boolean_t have_row;
648 int working_depth;
649 svn_wc__db_status_t presence;
650 const char *moved_to;
651
652 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
653 STMT_SELECT_LOWEST_WORKING_NODE));
654 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
655 op_depth));
656 SVN_ERR(svn_sqlite__step(&have_row, stmt));
657
658 if (!have_row)
659 return svn_error_trace(svn_sqlite__reset(stmt));
660
661 working_depth = svn_sqlite__column_int(stmt, 0);
662 presence = svn_sqlite__column_token(stmt, 1, presence_map);
663 moved_to = svn_sqlite__column_text(stmt, 3, scratch_pool);
664
665 SVN_ERR(svn_sqlite__reset(stmt));
666
667 if (moved_to)
668 {
669 /* Turn the move into a copy to keep the NODES table valid */
670 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
671 STMT_CLEAR_MOVED_HERE_RECURSIVE));
672 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
673 moved_to, relpath_depth(moved_to)));
674 SVN_ERR(svn_sqlite__step_done(stmt));
675
676 /* This leaves just the moved_to information on the origin,
677 which we will remove in the next step */
678 }
679
680 if (presence == svn_wc__db_status_base_deleted)
681 {
682 /* Nothing left to shadow; remove the base-deleted node */
683 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_NODE));
684 }
685 else if (moved_to)
686 {
687 /* Clear moved to information, as this node is no longer base-deleted */
688 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
689 STMT_CLEAR_MOVED_TO_RELPATH));
690 }
691 else
692 {
693 /* Nothing to update */
694 return SVN_NO_ERROR;
695 }
696
697 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
698 working_depth));
699
700 return svn_error_trace(svn_sqlite__update(NULL, stmt));
701 }
702
703
704
705 /* Insert the base row represented by (insert_base_baton_t *) BATON. */
706 static svn_error_t *
insert_base_node(const insert_base_baton_t * pibb,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)707 insert_base_node(const insert_base_baton_t *pibb,
708 svn_wc__db_wcroot_t *wcroot,
709 const char *local_relpath,
710 apr_pool_t *scratch_pool)
711 {
712 apr_int64_t repos_id = pibb->repos_id;
713 svn_sqlite__stmt_t *stmt;
714 svn_filesize_t recorded_size = SVN_INVALID_FILESIZE;
715 apr_int64_t recorded_time;
716 svn_boolean_t present;
717
718 /* The directory at the WCROOT has a NULL parent_relpath. Otherwise,
719 bind the appropriate parent_relpath. */
720 const char *parent_relpath =
721 (*local_relpath == '\0') ? NULL
722 : svn_relpath_dirname(local_relpath, scratch_pool);
723
724 if (pibb->repos_id == INVALID_REPOS_ID)
725 SVN_ERR(create_repos_id(&repos_id, pibb->repos_root_url, pibb->repos_uuid,
726 wcroot->sdb, scratch_pool));
727
728 SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
729 SVN_ERR_ASSERT(pibb->repos_relpath != NULL);
730
731 if (pibb->keep_recorded_info)
732 {
733 svn_boolean_t have_row;
734 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
735 STMT_SELECT_BASE_NODE));
736 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
737 SVN_ERR(svn_sqlite__step(&have_row, stmt));
738 if (have_row)
739 {
740 /* Preserve size and modification time if caller asked us to. */
741 recorded_size = get_recorded_size(stmt, 6);
742 recorded_time = svn_sqlite__column_int64(stmt, 12);
743 }
744 SVN_ERR(svn_sqlite__reset(stmt));
745 }
746
747 present = (pibb->status == svn_wc__db_status_normal
748 || pibb->status == svn_wc__db_status_incomplete);
749
750 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
751 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisr"
752 "tstr" /* 8 - 11 */
753 "isnnnnns", /* 12 - 19 */
754 wcroot->wc_id, /* 1 */
755 local_relpath, /* 2 */
756 0, /* op_depth is 0 for base */
757 parent_relpath, /* 4 */
758 repos_id,
759 pibb->repos_relpath,
760 pibb->revision,
761 presence_map, pibb->status, /* 8 */
762 (pibb->kind == svn_node_dir && present) /* 9 */
763 ? svn_token__to_word(depth_map, pibb->depth)
764 : NULL,
765 kind_map, pibb->kind, /* 10 */
766 pibb->changed_rev, /* 11 */
767 pibb->changed_date, /* 12 */
768 pibb->changed_author, /* 13 */
769 (pibb->kind == svn_node_symlink && present) ?
770 pibb->target : NULL)); /* 19 */
771 if (pibb->kind == svn_node_file && present)
772 {
773 if (!pibb->checksum
774 && pibb->status != svn_wc__db_status_not_present
775 && pibb->status != svn_wc__db_status_excluded
776 && pibb->status != svn_wc__db_status_server_excluded)
777 return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
778 _("The file '%s' has no checksum."),
779 path_for_error_message(wcroot, local_relpath,
780 scratch_pool));
781
782 SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, pibb->checksum,
783 scratch_pool));
784
785 if (recorded_size != SVN_INVALID_FILESIZE)
786 {
787 SVN_ERR(svn_sqlite__bind_int64(stmt, 16, recorded_size));
788 SVN_ERR(svn_sqlite__bind_int64(stmt, 17, recorded_time));
789 }
790 }
791
792 /* Set properties. Must be null if presence not normal or incomplete. */
793 assert(pibb->status == svn_wc__db_status_normal
794 || pibb->status == svn_wc__db_status_incomplete
795 || pibb->props == NULL);
796 if (present)
797 {
798 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, pibb->props,
799 scratch_pool));
800
801 SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, pibb->iprops,
802 scratch_pool));
803 }
804
805 if (pibb->dav_cache)
806 SVN_ERR(svn_sqlite__bind_properties(stmt, 18, pibb->dav_cache,
807 scratch_pool));
808
809 if (pibb->file_external)
810 SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
811
812 SVN_ERR(svn_sqlite__insert(NULL, stmt));
813
814 if (pibb->update_actual_props)
815 {
816 /* Cast away const, to allow calling property helpers */
817 apr_hash_t *base_props = (apr_hash_t *)pibb->props;
818 apr_hash_t *new_actual_props = (apr_hash_t *)pibb->new_actual_props;
819
820 if (base_props != NULL
821 && new_actual_props != NULL
822 && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
823 {
824 apr_array_header_t *diffs;
825
826 SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
827 scratch_pool));
828
829 if (diffs->nelts == 0)
830 new_actual_props = NULL;
831 }
832
833 SVN_ERR(set_actual_props(wcroot, local_relpath, new_actual_props,
834 scratch_pool));
835 }
836
837 if (pibb->kind == svn_node_dir && pibb->children)
838 SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
839 local_relpath,
840 repos_id,
841 pibb->repos_relpath,
842 pibb->revision,
843 pibb->children,
844 0 /* BASE */,
845 scratch_pool));
846
847 /* When this is not the root node, check shadowing behavior */
848 if (*local_relpath)
849 {
850 if (parent_relpath
851 && ((pibb->status == svn_wc__db_status_normal)
852 || (pibb->status == svn_wc__db_status_incomplete))
853 && ! pibb->file_external)
854 {
855 SVN_ERR(db_extend_parent_delete(wcroot, local_relpath,
856 pibb->kind, 0,
857 scratch_pool));
858 }
859 else if (pibb->status == svn_wc__db_status_not_present
860 || pibb->status == svn_wc__db_status_server_excluded
861 || pibb->status == svn_wc__db_status_excluded)
862 {
863 SVN_ERR(db_retract_parent_delete(wcroot, local_relpath, 0,
864 scratch_pool));
865 }
866 }
867
868 if (pibb->delete_working)
869 {
870 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
871 STMT_DELETE_WORKING_NODE));
872 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
873 SVN_ERR(svn_sqlite__step_done(stmt));
874 }
875 if (pibb->insert_base_deleted)
876 {
877 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
878 STMT_INSERT_DELETE_FROM_BASE));
879 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
880 wcroot->wc_id, local_relpath,
881 relpath_depth(local_relpath)));
882 SVN_ERR(svn_sqlite__step_done(stmt));
883 }
884
885 SVN_ERR(add_work_items(wcroot->sdb, pibb->work_items, scratch_pool));
886 if (pibb->conflict)
887 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
888 pibb->conflict, scratch_pool));
889
890 return SVN_NO_ERROR;
891 }
892
893
894 /* Initialize the baton with appropriate "blank" values. This allows the
895 insertion function to leave certain columns null. */
896 static void
blank_iwb(insert_working_baton_t * piwb)897 blank_iwb(insert_working_baton_t *piwb)
898 {
899 memset(piwb, 0, sizeof(*piwb));
900 piwb->changed_rev = SVN_INVALID_REVNUM;
901 piwb->depth = svn_depth_infinity;
902
903 /* ORIGINAL_REPOS_ID and ORIGINAL_REVNUM could use some kind of "nil"
904 value, but... meh. We'll avoid them if ORIGINAL_REPOS_RELPATH==NULL. */
905 }
906
907
908 /* Insert a row in NODES for each (const char *) child name in CHILDREN,
909 whose parent directory is LOCAL_RELPATH, at op_depth=OP_DEPTH. Set each
910 child's presence to 'incomplete', kind to 'unknown', repos_id to REPOS_ID,
911 repos_path by appending the child name to REPOS_PATH, and revision to
912 REVISION (which should match the parent's revision).
913
914 If REPOS_ID is INVALID_REPOS_ID, set each child's repos_id to null. */
915 static svn_error_t *
insert_incomplete_children(svn_sqlite__db_t * sdb,apr_int64_t wc_id,const char * local_relpath,apr_int64_t repos_id,const char * repos_path,svn_revnum_t revision,const apr_array_header_t * children,int op_depth,apr_pool_t * scratch_pool)916 insert_incomplete_children(svn_sqlite__db_t *sdb,
917 apr_int64_t wc_id,
918 const char *local_relpath,
919 apr_int64_t repos_id,
920 const char *repos_path,
921 svn_revnum_t revision,
922 const apr_array_header_t *children,
923 int op_depth,
924 apr_pool_t *scratch_pool)
925 {
926 svn_sqlite__stmt_t *stmt;
927 int i;
928 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
929 apr_hash_t *moved_to_relpaths = apr_hash_make(scratch_pool);
930
931 SVN_ERR_ASSERT(repos_path != NULL || op_depth > 0);
932 SVN_ERR_ASSERT((repos_id != INVALID_REPOS_ID)
933 == (repos_path != NULL));
934
935 /* If we're inserting WORKING nodes, we might be replacing existing
936 * nodes which were moved-away. We need to retain the moved-to relpath of
937 * such nodes in order not to lose move information during replace. */
938 if (op_depth > 0)
939 {
940 for (i = children->nelts; i--; )
941 {
942 const char *name = APR_ARRAY_IDX(children, i, const char *);
943 svn_boolean_t have_row;
944
945 svn_pool_clear(iterpool);
946
947 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
948 STMT_SELECT_WORKING_NODE));
949 SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id,
950 svn_relpath_join(local_relpath, name,
951 iterpool)));
952 SVN_ERR(svn_sqlite__step(&have_row, stmt));
953 if (have_row && !svn_sqlite__column_is_null(stmt, 14))
954 svn_hash_sets(moved_to_relpaths, name,
955 svn_sqlite__column_text(stmt, 14, scratch_pool));
956
957 SVN_ERR(svn_sqlite__reset(stmt));
958 }
959 }
960
961 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
962
963 for (i = children->nelts; i--; )
964 {
965 const char *name = APR_ARRAY_IDX(children, i, const char *);
966
967 svn_pool_clear(iterpool);
968
969 SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnrsnsnnnnnnnnnnsn",
970 wc_id,
971 svn_relpath_join(local_relpath, name,
972 iterpool),
973 op_depth,
974 local_relpath,
975 revision,
976 "incomplete", /* 8, presence */
977 "unknown", /* 10, kind */
978 /* 21, moved_to */
979 svn_hash_gets(moved_to_relpaths, name)));
980 if (repos_id != INVALID_REPOS_ID)
981 {
982 SVN_ERR(svn_sqlite__bind_int64(stmt, 5, repos_id));
983 SVN_ERR(svn_sqlite__bind_text(stmt, 6,
984 svn_relpath_join(repos_path, name,
985 iterpool)));
986 }
987
988 SVN_ERR(svn_sqlite__insert(NULL, stmt));
989 }
990
991 svn_pool_destroy(iterpool);
992
993 return SVN_NO_ERROR;
994 }
995
996
997 /* Insert the working row represented by (insert_working_baton_t *) BATON. */
998 static svn_error_t *
insert_working_node(const insert_working_baton_t * piwb,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)999 insert_working_node(const insert_working_baton_t *piwb,
1000 svn_wc__db_wcroot_t *wcroot,
1001 const char *local_relpath,
1002 apr_pool_t *scratch_pool)
1003 {
1004 const char *parent_relpath;
1005 const char *moved_to_relpath = NULL;
1006 svn_sqlite__stmt_t *stmt;
1007 svn_boolean_t have_row;
1008 svn_boolean_t present;
1009
1010 SVN_ERR_ASSERT(piwb->op_depth > 0);
1011
1012 /* We cannot insert a WORKING_NODE row at the wcroot. */
1013 SVN_ERR_ASSERT(*local_relpath != '\0');
1014 parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
1015
1016 /* Preserve existing moved-to information for this relpath,
1017 * which might exist in case we're replacing an existing base-deleted
1018 * node. */
1019 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
1020 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1021 piwb->op_depth));
1022 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1023 if (have_row)
1024 moved_to_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
1025 SVN_ERR(svn_sqlite__reset(stmt));
1026
1027 present = (piwb->presence == svn_wc__db_status_normal
1028 || piwb->presence == svn_wc__db_status_incomplete);
1029
1030 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
1031 SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnntstrisn"
1032 "nnnn" /* properties translated_size last_mod_time dav_cache */
1033 "sns", /* symlink_target, file_external, moved_to */
1034 wcroot->wc_id, local_relpath,
1035 piwb->op_depth,
1036 parent_relpath,
1037 presence_map, piwb->presence,
1038 (piwb->kind == svn_node_dir && present)
1039 ? svn_token__to_word(depth_map, piwb->depth) : NULL,
1040 kind_map, piwb->kind,
1041 piwb->changed_rev,
1042 piwb->changed_date,
1043 piwb->changed_author,
1044 /* Note: incomplete nodes may have a NULL target. */
1045 (piwb->kind == svn_node_symlink && present)
1046 ? piwb->target : NULL,
1047 moved_to_relpath));
1048
1049 if (piwb->moved_here)
1050 {
1051 SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
1052 }
1053
1054 if (piwb->kind == svn_node_file && present)
1055 {
1056 SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, piwb->checksum,
1057 scratch_pool));
1058 }
1059
1060 if (piwb->original_repos_relpath != NULL)
1061 {
1062 SVN_ERR(svn_sqlite__bind_int64(stmt, 5, piwb->original_repos_id));
1063 SVN_ERR(svn_sqlite__bind_text(stmt, 6, piwb->original_repos_relpath));
1064 SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, piwb->original_revnum));
1065 }
1066
1067 /* Set properties. Must be null if presence not normal or incomplete. */
1068 assert(piwb->presence == svn_wc__db_status_normal
1069 || piwb->presence == svn_wc__db_status_incomplete
1070 || piwb->props == NULL);
1071 if (present && piwb->original_repos_relpath)
1072 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, piwb->props, scratch_pool));
1073
1074 SVN_ERR(svn_sqlite__insert(NULL, stmt));
1075
1076 /* Insert incomplete children, if specified.
1077 The children are part of the same op and so have the same op_depth.
1078 (The only time we'd want a different depth is during a recursive
1079 simple add, but we never insert children here during a simple add.) */
1080 if (piwb->kind == svn_node_dir && piwb->children)
1081 SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
1082 local_relpath,
1083 INVALID_REPOS_ID /* inherit repos_id */,
1084 NULL /* inherit repos_path */,
1085 piwb->original_revnum,
1086 piwb->children,
1087 piwb->op_depth,
1088 scratch_pool));
1089
1090 if (piwb->update_actual_props)
1091 {
1092 /* Cast away const, to allow calling property helpers */
1093 apr_hash_t *base_props = (apr_hash_t *)piwb->props;
1094 apr_hash_t *new_actual_props = (apr_hash_t *)piwb->new_actual_props;
1095
1096 if (base_props != NULL
1097 && new_actual_props != NULL
1098 && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
1099 {
1100 apr_array_header_t *diffs;
1101
1102 SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
1103 scratch_pool));
1104
1105 if (diffs->nelts == 0)
1106 new_actual_props = NULL;
1107 }
1108
1109 SVN_ERR(set_actual_props(wcroot, local_relpath, new_actual_props,
1110 scratch_pool));
1111 }
1112
1113 if (piwb->kind == svn_node_dir)
1114 {
1115 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1116 STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST));
1117 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1118 SVN_ERR(svn_sqlite__step_done(stmt));
1119
1120 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1121 STMT_DELETE_ACTUAL_EMPTY));
1122 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1123 SVN_ERR(svn_sqlite__step_done(stmt));
1124 }
1125
1126 if (piwb->not_present_op_depth > 0
1127 && piwb->not_present_op_depth < piwb->op_depth)
1128 {
1129 /* And also insert a not-present node to tell the commit processing that
1130 a child of the parent node was not copied. */
1131 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1132 STMT_INSERT_NODE));
1133
1134 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
1135 wcroot->wc_id, local_relpath,
1136 piwb->not_present_op_depth, parent_relpath,
1137 piwb->original_repos_id,
1138 piwb->original_repos_relpath,
1139 piwb->original_revnum,
1140 presence_map, svn_wc__db_status_not_present,
1141 /* NULL */
1142 kind_map, piwb->kind));
1143
1144 SVN_ERR(svn_sqlite__step_done(stmt));
1145 }
1146
1147 SVN_ERR(add_work_items(wcroot->sdb, piwb->work_items, scratch_pool));
1148 if (piwb->conflict)
1149 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
1150 piwb->conflict, scratch_pool));
1151
1152 return SVN_NO_ERROR;
1153 }
1154
1155
1156 /* Return in *CHILDREN all of the children of the directory LOCAL_RELPATH,
1157 of any status, in all op-depths in the NODES table. */
1158 static svn_error_t *
gather_children(const apr_array_header_t ** children,svn_wc__db_wcroot_t * wcroot,const char * parent_relpath,int stmt_idx,int op_depth,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1159 gather_children(const apr_array_header_t **children,
1160 svn_wc__db_wcroot_t *wcroot,
1161 const char *parent_relpath,
1162 int stmt_idx,
1163 int op_depth,
1164 apr_pool_t *result_pool,
1165 apr_pool_t *scratch_pool)
1166 {
1167 apr_array_header_t *result;
1168 svn_sqlite__stmt_t *stmt;
1169 svn_boolean_t have_row;
1170
1171 result = apr_array_make(result_pool, 16, sizeof(const char*));
1172
1173 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
1174 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
1175 if (op_depth >= 0)
1176 SVN_ERR(svn_sqlite__bind_int(stmt, 3, op_depth));
1177
1178 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1179 while (have_row)
1180 {
1181 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1182 const char *name = svn_relpath_basename(child_relpath, result_pool);
1183
1184 APR_ARRAY_PUSH(result, const char *) = name;
1185
1186 SVN_ERR(svn_sqlite__step(&have_row, stmt));
1187 }
1188
1189 SVN_ERR(svn_sqlite__reset(stmt));
1190 *children = result;
1191 return SVN_NO_ERROR;
1192 }
1193
1194 /* Return TRUE if CHILD_ABSPATH is an immediate child of PARENT_ABSPATH.
1195 * Else, return FALSE. */
1196 static svn_boolean_t
is_immediate_child_path(const char * parent_abspath,const char * child_abspath)1197 is_immediate_child_path(const char *parent_abspath, const char *child_abspath)
1198 {
1199 const char *local_relpath = svn_dirent_skip_ancestor(parent_abspath,
1200 child_abspath);
1201
1202 /* To be an immediate child local_relpath should have one (not empty)
1203 component */
1204 return local_relpath && *local_relpath && !strchr(local_relpath, '/');
1205 }
1206
1207
1208 /* Remove the access baton for LOCAL_ABSPATH from ACCESS_CACHE. */
1209 static void
remove_from_access_cache(apr_hash_t * access_cache,const char * local_abspath)1210 remove_from_access_cache(apr_hash_t *access_cache,
1211 const char *local_abspath)
1212 {
1213 svn_wc_adm_access_t *adm_access;
1214
1215 adm_access = svn_hash_gets(access_cache, local_abspath);
1216 if (adm_access)
1217 svn_wc__adm_access_set_entries(adm_access, NULL);
1218 }
1219
1220
1221 /* Flush the access baton for LOCAL_ABSPATH, and any of its children up to
1222 * the specified DEPTH, from the access baton cache in WCROOT.
1223 * Also flush the access baton for the parent of LOCAL_ABSPATH.I
1224 *
1225 * This function must be called when the access baton cache goes stale,
1226 * i.e. data about LOCAL_ABSPATH will need to be read again from disk.
1227 *
1228 * Use SCRATCH_POOL for temporary allocations. */
1229 static svn_error_t *
flush_entries(svn_wc__db_wcroot_t * wcroot,const char * local_abspath,svn_depth_t depth,apr_pool_t * scratch_pool)1230 flush_entries(svn_wc__db_wcroot_t *wcroot,
1231 const char *local_abspath,
1232 svn_depth_t depth,
1233 apr_pool_t *scratch_pool)
1234 {
1235 const char *parent_abspath;
1236
1237 if (apr_hash_count(wcroot->access_cache) == 0)
1238 return SVN_NO_ERROR;
1239
1240 remove_from_access_cache(wcroot->access_cache, local_abspath);
1241
1242 if (depth > svn_depth_empty)
1243 {
1244 apr_hash_index_t *hi;
1245
1246 /* Flush access batons of children within the specified depth. */
1247 for (hi = apr_hash_first(scratch_pool, wcroot->access_cache);
1248 hi;
1249 hi = apr_hash_next(hi))
1250 {
1251 const char *item_abspath = apr_hash_this_key(hi);
1252
1253 if ((depth == svn_depth_files || depth == svn_depth_immediates) &&
1254 is_immediate_child_path(local_abspath, item_abspath))
1255 {
1256 remove_from_access_cache(wcroot->access_cache, item_abspath);
1257 }
1258 else if (depth == svn_depth_infinity &&
1259 svn_dirent_is_ancestor(local_abspath, item_abspath))
1260 {
1261 remove_from_access_cache(wcroot->access_cache, item_abspath);
1262 }
1263 }
1264 }
1265
1266 /* We're going to be overly aggressive here and just flush the parent
1267 without doing much checking. This may hurt performance for
1268 legacy API consumers, but that's not our problem. :) */
1269 parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
1270 remove_from_access_cache(wcroot->access_cache, parent_abspath);
1271
1272 return SVN_NO_ERROR;
1273 }
1274
1275
1276 /* Add a single WORK_ITEM into the given SDB's WORK_QUEUE table. This does
1277 not perform its work within a transaction, assuming the caller will
1278 manage that. */
1279 static svn_error_t *
add_single_work_item(svn_sqlite__db_t * sdb,const svn_skel_t * work_item,apr_pool_t * scratch_pool)1280 add_single_work_item(svn_sqlite__db_t *sdb,
1281 const svn_skel_t *work_item,
1282 apr_pool_t *scratch_pool)
1283 {
1284 svn_stringbuf_t *serialized;
1285 svn_sqlite__stmt_t *stmt;
1286
1287 serialized = svn_skel__unparse(work_item, scratch_pool);
1288 SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_WORK_ITEM));
1289 SVN_ERR(svn_sqlite__bind_blob(stmt, 1, serialized->data, serialized->len));
1290 return svn_error_trace(svn_sqlite__insert(NULL, stmt));
1291 }
1292
1293
1294 /* Add work item(s) to the given SDB. Also see add_single_work_item(). This
1295 SKEL is usually passed to the various wc_db operation functions. It may
1296 be NULL, indicating no additional work items are needed, it may be a
1297 single work item, or it may be a list of work items. */
1298 static svn_error_t *
add_work_items(svn_sqlite__db_t * sdb,const svn_skel_t * skel,apr_pool_t * scratch_pool)1299 add_work_items(svn_sqlite__db_t *sdb,
1300 const svn_skel_t *skel,
1301 apr_pool_t *scratch_pool)
1302 {
1303 apr_pool_t *iterpool;
1304
1305 /* Maybe there are no work items to insert. */
1306 if (skel == NULL)
1307 return SVN_NO_ERROR;
1308
1309 /* Should have a list. */
1310 SVN_ERR_ASSERT(!skel->is_atom);
1311
1312 /* Is the list a single work item? Or a list of work items? */
1313 if (SVN_WC__SINGLE_WORK_ITEM(skel))
1314 return svn_error_trace(add_single_work_item(sdb, skel, scratch_pool));
1315
1316 /* SKEL is a list-of-lists, aka list of work items. */
1317
1318 iterpool = svn_pool_create(scratch_pool);
1319 for (skel = skel->children; skel; skel = skel->next)
1320 {
1321 svn_pool_clear(iterpool);
1322
1323 SVN_ERR(add_single_work_item(sdb, skel, iterpool));
1324 }
1325 svn_pool_destroy(iterpool);
1326
1327 return SVN_NO_ERROR;
1328 }
1329
1330
1331 /* Determine whether the node exists for a given WCROOT and LOCAL_RELPATH. */
1332 static svn_error_t *
does_node_exist(svn_boolean_t * exists,const svn_wc__db_wcroot_t * wcroot,const char * local_relpath)1333 does_node_exist(svn_boolean_t *exists,
1334 const svn_wc__db_wcroot_t *wcroot,
1335 const char *local_relpath)
1336 {
1337 svn_sqlite__stmt_t *stmt;
1338
1339 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DOES_NODE_EXIST));
1340 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1341 SVN_ERR(svn_sqlite__step(exists, stmt));
1342
1343 return svn_error_trace(svn_sqlite__reset(stmt));
1344 }
1345
1346 svn_error_t *
svn_wc__db_install_schema_statistics(svn_sqlite__db_t * sdb,apr_pool_t * scratch_pool)1347 svn_wc__db_install_schema_statistics(svn_sqlite__db_t *sdb,
1348 apr_pool_t *scratch_pool)
1349 {
1350 SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_INSTALL_SCHEMA_STATISTICS));
1351
1352 return SVN_NO_ERROR;
1353 }
1354
1355 /* Helper for create_db(). Initializes our wc.db schema.
1356 */
1357 static svn_error_t *
init_db(apr_int64_t * repos_id,apr_int64_t * wc_id,svn_sqlite__db_t * db,const char * repos_root_url,const char * repos_uuid,const char * root_node_repos_relpath,svn_revnum_t root_node_revision,svn_depth_t root_node_depth,apr_pool_t * scratch_pool)1358 init_db(/* output values */
1359 apr_int64_t *repos_id,
1360 apr_int64_t *wc_id,
1361 /* input values */
1362 svn_sqlite__db_t *db,
1363 const char *repos_root_url,
1364 const char *repos_uuid,
1365 const char *root_node_repos_relpath,
1366 svn_revnum_t root_node_revision,
1367 svn_depth_t root_node_depth,
1368 apr_pool_t *scratch_pool)
1369 {
1370 svn_sqlite__stmt_t *stmt;
1371
1372 /* Create the database's schema. */
1373 SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_SCHEMA));
1374
1375 SVN_ERR(svn_wc__db_install_schema_statistics(db, scratch_pool));
1376
1377 /* Insert the repository. */
1378 SVN_ERR(create_repos_id(repos_id, repos_root_url, repos_uuid,
1379 db, scratch_pool));
1380
1381 /* Insert the wcroot. */
1382 /* ### Right now, this just assumes wc metadata is being stored locally. */
1383 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT));
1384 SVN_ERR(svn_sqlite__insert(wc_id, stmt));
1385
1386 if (root_node_repos_relpath)
1387 {
1388 svn_wc__db_status_t status = svn_wc__db_status_normal;
1389
1390 if (root_node_revision > 0)
1391 status = svn_wc__db_status_incomplete; /* Will be filled by update */
1392
1393 SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_NODE));
1394 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtst",
1395 *wc_id, /* 1 */
1396 "", /* 2 */
1397 0, /* op_depth is 0 for base */
1398 SVN_VA_NULL, /* 4 */
1399 *repos_id,
1400 root_node_repos_relpath,
1401 root_node_revision,
1402 presence_map, status, /* 8 */
1403 svn_token__to_word(depth_map,
1404 root_node_depth),
1405 kind_map, svn_node_dir /* 10 */));
1406
1407 SVN_ERR(svn_sqlite__insert(NULL, stmt));
1408 }
1409
1410 return SVN_NO_ERROR;
1411 }
1412
1413 /* Create an sqlite database at DIR_ABSPATH/SDB_FNAME and insert
1414 records for REPOS_ID (using REPOS_ROOT_URL and REPOS_UUID) into
1415 REPOSITORY and for WC_ID into WCROOT. Return the DB connection
1416 in *SDB.
1417
1418 If ROOT_NODE_REPOS_RELPATH is not NULL, insert a BASE node at
1419 the working copy root with repository relpath ROOT_NODE_REPOS_RELPATH,
1420 revision ROOT_NODE_REVISION and depth ROOT_NODE_DEPTH.
1421 */
1422 static svn_error_t *
create_db(svn_sqlite__db_t ** sdb,apr_int64_t * repos_id,apr_int64_t * wc_id,const char * dir_abspath,const char * repos_root_url,const char * repos_uuid,const char * sdb_fname,const char * root_node_repos_relpath,svn_revnum_t root_node_revision,svn_depth_t root_node_depth,svn_boolean_t exclusive,apr_int32_t timeout,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1423 create_db(svn_sqlite__db_t **sdb,
1424 apr_int64_t *repos_id,
1425 apr_int64_t *wc_id,
1426 const char *dir_abspath,
1427 const char *repos_root_url,
1428 const char *repos_uuid,
1429 const char *sdb_fname,
1430 const char *root_node_repos_relpath,
1431 svn_revnum_t root_node_revision,
1432 svn_depth_t root_node_depth,
1433 svn_boolean_t exclusive,
1434 apr_int32_t timeout,
1435 apr_pool_t *result_pool,
1436 apr_pool_t *scratch_pool)
1437 {
1438 SVN_ERR(svn_wc__db_util_open_db(sdb, dir_abspath, sdb_fname,
1439 svn_sqlite__mode_rwcreate, exclusive,
1440 timeout,
1441 NULL /* my_statements */,
1442 result_pool, scratch_pool));
1443
1444 SVN_SQLITE__WITH_LOCK(init_db(repos_id, wc_id,
1445 *sdb, repos_root_url, repos_uuid,
1446 root_node_repos_relpath, root_node_revision,
1447 root_node_depth, scratch_pool),
1448 *sdb);
1449
1450 return SVN_NO_ERROR;
1451 }
1452
1453
1454 svn_error_t *
svn_wc__db_init(svn_wc__db_t * db,const char * local_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t initial_rev,svn_depth_t depth,apr_pool_t * scratch_pool)1455 svn_wc__db_init(svn_wc__db_t *db,
1456 const char *local_abspath,
1457 const char *repos_relpath,
1458 const char *repos_root_url,
1459 const char *repos_uuid,
1460 svn_revnum_t initial_rev,
1461 svn_depth_t depth,
1462 apr_pool_t *scratch_pool)
1463 {
1464 svn_sqlite__db_t *sdb;
1465 apr_int64_t repos_id;
1466 apr_int64_t wc_id;
1467 svn_wc__db_wcroot_t *wcroot;
1468 svn_boolean_t sqlite_exclusive = FALSE;
1469 apr_int32_t sqlite_timeout = 0; /* default timeout */
1470 apr_hash_index_t *hi;
1471
1472 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1473 SVN_ERR_ASSERT(repos_relpath != NULL);
1474 SVN_ERR_ASSERT(depth == svn_depth_empty
1475 || depth == svn_depth_files
1476 || depth == svn_depth_immediates
1477 || depth == svn_depth_infinity);
1478
1479 /* ### REPOS_ROOT_URL and REPOS_UUID may be NULL. ... more doc: tbd */
1480
1481 SVN_ERR(svn_config_get_bool(db->config, &sqlite_exclusive,
1482 SVN_CONFIG_SECTION_WORKING_COPY,
1483 SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
1484 FALSE));
1485
1486 /* Create the SDB and insert the basic rows. */
1487 SVN_ERR(create_db(&sdb, &repos_id, &wc_id, local_abspath, repos_root_url,
1488 repos_uuid, SDB_FILE,
1489 repos_relpath, initial_rev, depth, sqlite_exclusive,
1490 sqlite_timeout,
1491 db->state_pool, scratch_pool));
1492
1493 /* Create the WCROOT for this directory. */
1494 SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
1495 apr_pstrdup(db->state_pool, local_abspath),
1496 sdb, wc_id, FORMAT_FROM_SDB,
1497 FALSE /* auto-upgrade */,
1498 db->state_pool, scratch_pool));
1499
1500 /* Any previously cached children may now have a new WCROOT, most likely that
1501 of the new WCROOT, but there might be descendant directories that are their
1502 own working copy, in which case setting WCROOT to our new WCROOT might
1503 actually break things for those.
1504
1505 Clearing is the safest thing we can do in this case, as a test would lead
1506 to unnecessary probing, while the standard code probes later anyway. So we
1507 only lose a bit of memory
1508
1509 ### Perhaps we could check wcroot->abspath to detect which case we have
1510 where, but currently it is already very hard to trigger this from
1511 the short living 'svn' client. (GUI clients like TortoiseSVN are far
1512 more likely to get in these cases)
1513 */
1514 for (hi = apr_hash_first(scratch_pool, db->dir_data);
1515 hi;
1516 hi = apr_hash_next(hi))
1517 {
1518 const char *abspath = apr_hash_this_key(hi);
1519 if (svn_dirent_is_ancestor(wcroot->abspath, abspath))
1520 svn_hash_sets(db->dir_data, abspath, NULL);
1521 }
1522
1523 /* The WCROOT is complete. Stash it into DB. */
1524 svn_hash_sets(db->dir_data, wcroot->abspath, wcroot);
1525
1526 return SVN_NO_ERROR;
1527 }
1528
1529
1530 svn_error_t *
svn_wc__db_to_relpath(const char ** local_relpath,svn_wc__db_t * db,const char * wri_abspath,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1531 svn_wc__db_to_relpath(const char **local_relpath,
1532 svn_wc__db_t *db,
1533 const char *wri_abspath,
1534 const char *local_abspath,
1535 apr_pool_t *result_pool,
1536 apr_pool_t *scratch_pool)
1537 {
1538 svn_wc__db_wcroot_t *wcroot;
1539 const char *relpath;
1540
1541 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1542
1543 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &relpath, db,
1544 wri_abspath, result_pool, scratch_pool));
1545
1546 /* This function is indirectly called from the upgrade code, so we
1547 can't verify the wcroot here. Just check that it is not NULL */
1548 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1549
1550 if (svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
1551 {
1552 *local_relpath = apr_pstrdup(result_pool,
1553 svn_dirent_skip_ancestor(wcroot->abspath,
1554 local_abspath));
1555 }
1556 else
1557 /* Probably moving from $TMP. Should we allow this? */
1558 *local_relpath = apr_pstrdup(result_pool, local_abspath);
1559
1560 return SVN_NO_ERROR;
1561 }
1562
1563
1564 svn_error_t *
svn_wc__db_from_relpath(const char ** local_abspath,svn_wc__db_t * db,const char * wri_abspath,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1565 svn_wc__db_from_relpath(const char **local_abspath,
1566 svn_wc__db_t *db,
1567 const char *wri_abspath,
1568 const char *local_relpath,
1569 apr_pool_t *result_pool,
1570 apr_pool_t *scratch_pool)
1571 {
1572 svn_wc__db_wcroot_t *wcroot;
1573 const char *unused_relpath;
1574 #if 0
1575 SVN_ERR_ASSERT(svn_relpath_is_canonical(local_relpath));
1576 #endif
1577
1578 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1579 wri_abspath, scratch_pool, scratch_pool));
1580
1581 /* This function is indirectly called from the upgrade code, so we
1582 can't verify the wcroot here. Just check that it is not NULL */
1583 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1584
1585
1586 *local_abspath = svn_dirent_join(wcroot->abspath,
1587 local_relpath,
1588 result_pool);
1589 return SVN_NO_ERROR;
1590 }
1591
1592
1593 svn_error_t *
svn_wc__db_get_wcroot(const char ** wcroot_abspath,svn_wc__db_t * db,const char * wri_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)1594 svn_wc__db_get_wcroot(const char **wcroot_abspath,
1595 svn_wc__db_t *db,
1596 const char *wri_abspath,
1597 apr_pool_t *result_pool,
1598 apr_pool_t *scratch_pool)
1599 {
1600 svn_wc__db_wcroot_t *wcroot;
1601 const char *unused_relpath;
1602
1603 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1604 wri_abspath, scratch_pool, scratch_pool));
1605
1606 /* Can't use VERIFY_USABLE_WCROOT, as this should be usable to detect
1607 where call upgrade */
1608 CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1609
1610 *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
1611
1612 return SVN_NO_ERROR;
1613 }
1614
1615
1616 svn_error_t *
svn_wc__db_base_add_directory(svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t revision,const apr_hash_t * props,svn_revnum_t changed_rev,apr_time_t changed_date,const char * changed_author,const apr_array_header_t * children,svn_depth_t depth,apr_hash_t * dav_cache,svn_boolean_t update_actual_props,apr_hash_t * new_actual_props,apr_array_header_t * new_iprops,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)1617 svn_wc__db_base_add_directory(svn_wc__db_t *db,
1618 const char *local_abspath,
1619 const char *wri_abspath,
1620 const char *repos_relpath,
1621 const char *repos_root_url,
1622 const char *repos_uuid,
1623 svn_revnum_t revision,
1624 const apr_hash_t *props,
1625 svn_revnum_t changed_rev,
1626 apr_time_t changed_date,
1627 const char *changed_author,
1628 const apr_array_header_t *children,
1629 svn_depth_t depth,
1630 apr_hash_t *dav_cache,
1631 svn_boolean_t update_actual_props,
1632 apr_hash_t *new_actual_props,
1633 apr_array_header_t *new_iprops,
1634 const svn_skel_t *conflict,
1635 const svn_skel_t *work_items,
1636 apr_pool_t *scratch_pool)
1637 {
1638 svn_wc__db_wcroot_t *wcroot;
1639 const char *local_relpath;
1640 insert_base_baton_t ibb;
1641
1642 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1643 SVN_ERR_ASSERT(repos_relpath != NULL);
1644 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1645 SVN_ERR_ASSERT(repos_uuid != NULL);
1646 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1647 SVN_ERR_ASSERT(props != NULL);
1648 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1649 #if 0
1650 SVN_ERR_ASSERT(children != NULL);
1651 #endif
1652
1653 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1654 wri_abspath, scratch_pool, scratch_pool));
1655 VERIFY_USABLE_WCROOT(wcroot);
1656 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1657
1658 blank_ibb(&ibb);
1659
1660 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1661 ibb.repos_root_url = repos_root_url;
1662 ibb.repos_uuid = repos_uuid;
1663
1664 ibb.status = svn_wc__db_status_normal;
1665 ibb.kind = svn_node_dir;
1666 ibb.repos_relpath = repos_relpath;
1667 ibb.revision = revision;
1668
1669 ibb.iprops = new_iprops;
1670 ibb.props = props;
1671 ibb.changed_rev = changed_rev;
1672 ibb.changed_date = changed_date;
1673 ibb.changed_author = changed_author;
1674
1675 ibb.children = children;
1676 ibb.depth = depth;
1677
1678 ibb.dav_cache = dav_cache;
1679 ibb.conflict = conflict;
1680 ibb.work_items = work_items;
1681
1682 if (update_actual_props)
1683 {
1684 ibb.update_actual_props = TRUE;
1685 ibb.new_actual_props = new_actual_props;
1686 }
1687
1688 /* Insert the directory and all its children transactionally.
1689
1690 Note: old children can stick around, even if they are no longer present
1691 in this directory's revision. */
1692 SVN_WC__DB_WITH_TXN(
1693 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1694 wcroot);
1695
1696 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
1697 return SVN_NO_ERROR;
1698 }
1699
1700 svn_error_t *
svn_wc__db_base_add_incomplete_directory(svn_wc__db_t * db,const char * local_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t revision,svn_depth_t depth,svn_boolean_t insert_base_deleted,svn_boolean_t delete_working,svn_skel_t * conflict,svn_skel_t * work_items,apr_pool_t * scratch_pool)1701 svn_wc__db_base_add_incomplete_directory(svn_wc__db_t *db,
1702 const char *local_abspath,
1703 const char *repos_relpath,
1704 const char *repos_root_url,
1705 const char *repos_uuid,
1706 svn_revnum_t revision,
1707 svn_depth_t depth,
1708 svn_boolean_t insert_base_deleted,
1709 svn_boolean_t delete_working,
1710 svn_skel_t *conflict,
1711 svn_skel_t *work_items,
1712 apr_pool_t *scratch_pool)
1713 {
1714 svn_wc__db_wcroot_t *wcroot;
1715 const char *local_relpath;
1716 struct insert_base_baton_t ibb;
1717
1718 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1719 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1720 SVN_ERR_ASSERT(repos_relpath && repos_root_url && repos_uuid);
1721
1722 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
1723 db, local_abspath,
1724 scratch_pool, scratch_pool));
1725
1726 VERIFY_USABLE_WCROOT(wcroot);
1727
1728 blank_ibb(&ibb);
1729
1730 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1731 ibb.repos_root_url = repos_root_url;
1732 ibb.repos_uuid = repos_uuid;
1733
1734 ibb.status = svn_wc__db_status_incomplete;
1735 ibb.kind = svn_node_dir;
1736 ibb.repos_relpath = repos_relpath;
1737 ibb.revision = revision;
1738 ibb.depth = depth;
1739 ibb.insert_base_deleted = insert_base_deleted;
1740 ibb.delete_working = delete_working;
1741
1742 ibb.conflict = conflict;
1743 ibb.work_items = work_items;
1744
1745 SVN_WC__DB_WITH_TXN(
1746 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1747 wcroot);
1748
1749 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
1750
1751 return SVN_NO_ERROR;
1752 }
1753
1754
1755 svn_error_t *
svn_wc__db_base_add_file(svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t revision,const apr_hash_t * props,svn_revnum_t changed_rev,apr_time_t changed_date,const char * changed_author,const svn_checksum_t * checksum,apr_hash_t * dav_cache,svn_boolean_t delete_working,svn_boolean_t update_actual_props,apr_hash_t * new_actual_props,apr_array_header_t * new_iprops,svn_boolean_t keep_recorded_info,svn_boolean_t insert_base_deleted,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)1756 svn_wc__db_base_add_file(svn_wc__db_t *db,
1757 const char *local_abspath,
1758 const char *wri_abspath,
1759 const char *repos_relpath,
1760 const char *repos_root_url,
1761 const char *repos_uuid,
1762 svn_revnum_t revision,
1763 const apr_hash_t *props,
1764 svn_revnum_t changed_rev,
1765 apr_time_t changed_date,
1766 const char *changed_author,
1767 const svn_checksum_t *checksum,
1768 apr_hash_t *dav_cache,
1769 svn_boolean_t delete_working,
1770 svn_boolean_t update_actual_props,
1771 apr_hash_t *new_actual_props,
1772 apr_array_header_t *new_iprops,
1773 svn_boolean_t keep_recorded_info,
1774 svn_boolean_t insert_base_deleted,
1775 const svn_skel_t *conflict,
1776 const svn_skel_t *work_items,
1777 apr_pool_t *scratch_pool)
1778 {
1779 svn_wc__db_wcroot_t *wcroot;
1780 const char *local_relpath;
1781 insert_base_baton_t ibb;
1782
1783 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1784 SVN_ERR_ASSERT(repos_relpath != NULL);
1785 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1786 SVN_ERR_ASSERT(repos_uuid != NULL);
1787 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1788 SVN_ERR_ASSERT(props != NULL);
1789 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1790 SVN_ERR_ASSERT(checksum != NULL);
1791
1792 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1793 wri_abspath, scratch_pool, scratch_pool));
1794 VERIFY_USABLE_WCROOT(wcroot);
1795 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1796
1797 blank_ibb(&ibb);
1798
1799 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1800 ibb.repos_root_url = repos_root_url;
1801 ibb.repos_uuid = repos_uuid;
1802
1803 ibb.status = svn_wc__db_status_normal;
1804 ibb.kind = svn_node_file;
1805 ibb.repos_relpath = repos_relpath;
1806 ibb.revision = revision;
1807
1808 ibb.props = props;
1809 ibb.changed_rev = changed_rev;
1810 ibb.changed_date = changed_date;
1811 ibb.changed_author = changed_author;
1812
1813 ibb.checksum = checksum;
1814
1815 ibb.dav_cache = dav_cache;
1816 ibb.iprops = new_iprops;
1817
1818 if (update_actual_props)
1819 {
1820 ibb.update_actual_props = TRUE;
1821 ibb.new_actual_props = new_actual_props;
1822 }
1823
1824 ibb.keep_recorded_info = keep_recorded_info;
1825 ibb.insert_base_deleted = insert_base_deleted;
1826 ibb.delete_working = delete_working;
1827
1828 ibb.conflict = conflict;
1829 ibb.work_items = work_items;
1830
1831 SVN_WC__DB_WITH_TXN(
1832 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1833 wcroot);
1834
1835 /* If this used to be a directory we should remove children so pass
1836 * depth infinity. */
1837 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1838 scratch_pool));
1839 return SVN_NO_ERROR;
1840 }
1841
1842
1843 svn_error_t *
svn_wc__db_base_add_symlink(svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t revision,const apr_hash_t * props,svn_revnum_t changed_rev,apr_time_t changed_date,const char * changed_author,const char * target,apr_hash_t * dav_cache,svn_boolean_t delete_working,svn_boolean_t update_actual_props,apr_hash_t * new_actual_props,apr_array_header_t * new_iprops,svn_boolean_t keep_recorded_info,svn_boolean_t insert_base_deleted,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)1844 svn_wc__db_base_add_symlink(svn_wc__db_t *db,
1845 const char *local_abspath,
1846 const char *wri_abspath,
1847 const char *repos_relpath,
1848 const char *repos_root_url,
1849 const char *repos_uuid,
1850 svn_revnum_t revision,
1851 const apr_hash_t *props,
1852 svn_revnum_t changed_rev,
1853 apr_time_t changed_date,
1854 const char *changed_author,
1855 const char *target,
1856 apr_hash_t *dav_cache,
1857 svn_boolean_t delete_working,
1858 svn_boolean_t update_actual_props,
1859 apr_hash_t *new_actual_props,
1860 apr_array_header_t *new_iprops,
1861 svn_boolean_t keep_recorded_info,
1862 svn_boolean_t insert_base_deleted,
1863 const svn_skel_t *conflict,
1864 const svn_skel_t *work_items,
1865 apr_pool_t *scratch_pool)
1866 {
1867 svn_wc__db_wcroot_t *wcroot;
1868 const char *local_relpath;
1869 insert_base_baton_t ibb;
1870
1871 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1872 SVN_ERR_ASSERT(repos_relpath != NULL);
1873 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1874 SVN_ERR_ASSERT(repos_uuid != NULL);
1875 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1876 SVN_ERR_ASSERT(props != NULL);
1877 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1878 SVN_ERR_ASSERT(target != NULL);
1879
1880 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1881 wri_abspath, scratch_pool, scratch_pool));
1882 VERIFY_USABLE_WCROOT(wcroot);
1883 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1884 blank_ibb(&ibb);
1885
1886 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1887 ibb.repos_root_url = repos_root_url;
1888 ibb.repos_uuid = repos_uuid;
1889
1890 ibb.status = svn_wc__db_status_normal;
1891 ibb.kind = svn_node_symlink;
1892 ibb.repos_relpath = repos_relpath;
1893 ibb.revision = revision;
1894
1895 ibb.props = props;
1896 ibb.changed_rev = changed_rev;
1897 ibb.changed_date = changed_date;
1898 ibb.changed_author = changed_author;
1899
1900 ibb.target = target;
1901
1902 ibb.dav_cache = dav_cache;
1903 ibb.iprops = new_iprops;
1904
1905 if (update_actual_props)
1906 {
1907 ibb.update_actual_props = TRUE;
1908 ibb.new_actual_props = new_actual_props;
1909 }
1910
1911 ibb.keep_recorded_info = keep_recorded_info;
1912 ibb.insert_base_deleted = insert_base_deleted;
1913 ibb.delete_working = delete_working;
1914
1915 ibb.conflict = conflict;
1916 ibb.work_items = work_items;
1917
1918 SVN_WC__DB_WITH_TXN(
1919 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1920 wcroot);
1921
1922 /* If this used to be a directory we should remove children so pass
1923 * depth infinity. */
1924 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1925 scratch_pool));
1926 return SVN_NO_ERROR;
1927 }
1928
1929
1930 static svn_error_t *
add_excluded_or_not_present_node(svn_wc__db_t * db,const char * local_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t revision,svn_node_kind_t kind,svn_wc__db_status_t status,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)1931 add_excluded_or_not_present_node(svn_wc__db_t *db,
1932 const char *local_abspath,
1933 const char *repos_relpath,
1934 const char *repos_root_url,
1935 const char *repos_uuid,
1936 svn_revnum_t revision,
1937 svn_node_kind_t kind,
1938 svn_wc__db_status_t status,
1939 const svn_skel_t *conflict,
1940 const svn_skel_t *work_items,
1941 apr_pool_t *scratch_pool)
1942 {
1943 svn_wc__db_wcroot_t *wcroot;
1944 const char *local_relpath;
1945 insert_base_baton_t ibb;
1946 const char *dir_abspath, *name;
1947
1948 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1949 SVN_ERR_ASSERT(repos_relpath != NULL);
1950 SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1951 SVN_ERR_ASSERT(repos_uuid != NULL);
1952 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1953 SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
1954 || status == svn_wc__db_status_excluded
1955 || status == svn_wc__db_status_not_present);
1956
1957 /* These absent presence nodes are only useful below a parent node that is
1958 present. To avoid problems with working copies obstructing the child
1959 we calculate the wcroot and local_relpath of the parent and then add
1960 our own relpath. */
1961
1962 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
1963
1964 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1965 dir_abspath, scratch_pool, scratch_pool));
1966 VERIFY_USABLE_WCROOT(wcroot);
1967
1968 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
1969
1970 blank_ibb(&ibb);
1971
1972 /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1973 ibb.repos_root_url = repos_root_url;
1974 ibb.repos_uuid = repos_uuid;
1975
1976 ibb.status = status;
1977 ibb.kind = kind;
1978 ibb.repos_relpath = repos_relpath;
1979 ibb.revision = revision;
1980
1981 /* Depending upon KIND, any of these might get used. */
1982 ibb.children = NULL;
1983 ibb.depth = svn_depth_unknown;
1984 ibb.checksum = NULL;
1985 ibb.target = NULL;
1986
1987 ibb.conflict = conflict;
1988 ibb.work_items = work_items;
1989
1990 SVN_WC__DB_WITH_TXN(
1991 insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1992 wcroot);
1993
1994 /* If this used to be a directory we should remove children so pass
1995 * depth infinity. */
1996 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1997 scratch_pool));
1998
1999 return SVN_NO_ERROR;
2000 }
2001
2002
2003 svn_error_t *
svn_wc__db_base_add_excluded_node(svn_wc__db_t * db,const char * local_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t revision,svn_node_kind_t kind,svn_wc__db_status_t status,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)2004 svn_wc__db_base_add_excluded_node(svn_wc__db_t *db,
2005 const char *local_abspath,
2006 const char *repos_relpath,
2007 const char *repos_root_url,
2008 const char *repos_uuid,
2009 svn_revnum_t revision,
2010 svn_node_kind_t kind,
2011 svn_wc__db_status_t status,
2012 const svn_skel_t *conflict,
2013 const svn_skel_t *work_items,
2014 apr_pool_t *scratch_pool)
2015 {
2016 SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
2017 || status == svn_wc__db_status_excluded);
2018
2019 return add_excluded_or_not_present_node(
2020 db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2021 kind, status, conflict, work_items, scratch_pool);
2022 }
2023
2024
2025 svn_error_t *
svn_wc__db_base_add_not_present_node(svn_wc__db_t * db,const char * local_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t revision,svn_node_kind_t kind,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)2026 svn_wc__db_base_add_not_present_node(svn_wc__db_t *db,
2027 const char *local_abspath,
2028 const char *repos_relpath,
2029 const char *repos_root_url,
2030 const char *repos_uuid,
2031 svn_revnum_t revision,
2032 svn_node_kind_t kind,
2033 const svn_skel_t *conflict,
2034 const svn_skel_t *work_items,
2035 apr_pool_t *scratch_pool)
2036 {
2037 return add_excluded_or_not_present_node(
2038 db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2039 kind, svn_wc__db_status_not_present, conflict, work_items, scratch_pool);
2040 }
2041
2042 /* Recursively clear moved-here information at the copy-half of the move
2043 * which moved a node to MOVED_TO_RELPATH. This transforms this side of the
2044 * move into a simple copy.
2045 */
2046 static svn_error_t *
clear_moved_here(svn_wc__db_wcroot_t * wcroot,const char * moved_to_relpath,apr_pool_t * scratch_pool)2047 clear_moved_here(svn_wc__db_wcroot_t *wcroot,
2048 const char *moved_to_relpath,
2049 apr_pool_t *scratch_pool)
2050 {
2051 svn_sqlite__stmt_t *stmt;
2052 int affected_rows;
2053
2054 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2055 STMT_CLEAR_MOVED_HERE_RECURSIVE));
2056 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, moved_to_relpath,
2057 relpath_depth(moved_to_relpath)));
2058
2059 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2060
2061 if (affected_rows == 0)
2062 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2063 _("The node '%s' was not found."),
2064 path_for_error_message(wcroot, moved_to_relpath,
2065 scratch_pool));
2066
2067 return SVN_NO_ERROR;
2068 }
2069
2070 svn_error_t *
svn_wc__db_op_break_move_internal(svn_wc__db_wcroot_t * wcroot,const char * src_relpath,int delete_op_depth,const char * dst_relpath,const svn_skel_t * work_items,apr_pool_t * scratch_pool)2071 svn_wc__db_op_break_move_internal(svn_wc__db_wcroot_t *wcroot,
2072 const char *src_relpath,
2073 int delete_op_depth,
2074 const char *dst_relpath,
2075 const svn_skel_t *work_items,
2076 apr_pool_t *scratch_pool)
2077 {
2078 svn_sqlite__stmt_t *stmt;
2079 int affected;
2080
2081 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2082 STMT_CLEAR_MOVED_TO_RELPATH));
2083 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, src_relpath,
2084 delete_op_depth));
2085 SVN_ERR(svn_sqlite__update(&affected, stmt));
2086
2087 if (affected != 1)
2088 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2089 _("Path '%s' is not moved"),
2090 path_for_error_message(wcroot, src_relpath,
2091 scratch_pool));
2092
2093 SVN_ERR(clear_moved_here(wcroot, dst_relpath, scratch_pool));
2094
2095 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2096 return SVN_NO_ERROR;
2097 }
2098
2099
2100 /* The body of svn_wc__db_base_remove().
2101 */
2102 static svn_error_t *
db_base_remove(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_wc__db_t * db,svn_boolean_t keep_as_working,svn_boolean_t mark_not_present,svn_boolean_t mark_excluded,svn_revnum_t marker_revision,svn_skel_t * conflict,svn_skel_t * work_items,apr_pool_t * scratch_pool)2103 db_base_remove(svn_wc__db_wcroot_t *wcroot,
2104 const char *local_relpath,
2105 svn_wc__db_t *db, /* For checking conflicts */
2106 svn_boolean_t keep_as_working,
2107 svn_boolean_t mark_not_present,
2108 svn_boolean_t mark_excluded,
2109 svn_revnum_t marker_revision,
2110 svn_skel_t *conflict,
2111 svn_skel_t *work_items,
2112 apr_pool_t *scratch_pool)
2113 {
2114 svn_sqlite__stmt_t *stmt;
2115 svn_boolean_t have_row;
2116 svn_wc__db_status_t status;
2117 svn_revnum_t revision;
2118 apr_int64_t repos_id;
2119 const char *repos_relpath;
2120 svn_node_kind_t kind;
2121 svn_boolean_t keep_working;
2122 int op_depth;
2123 svn_node_kind_t wrk_kind;
2124 svn_boolean_t no_delete_wc = FALSE;
2125 svn_boolean_t file_external;
2126
2127 SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, &revision,
2128 &repos_relpath, &repos_id,
2129 NULL, NULL, NULL, NULL, NULL,
2130 NULL, NULL, NULL, NULL,
2131 &file_external,
2132 wcroot, local_relpath,
2133 scratch_pool, scratch_pool));
2134
2135 /* Check if there is already a working node */
2136 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2137 STMT_SELECT_NODE_INFO));
2138 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2139 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2140
2141 if (!have_row)
2142 return svn_error_trace(svn_sqlite__reset(stmt)); /* No BASE */
2143
2144 op_depth = svn_sqlite__column_int(stmt, 0);
2145 wrk_kind = svn_sqlite__column_token(stmt, 4, kind_map);
2146
2147 if (op_depth > 0
2148 && op_depth == relpath_depth(local_relpath))
2149 {
2150 svn_wc__db_status_t presence;
2151 presence = svn_sqlite__column_token(stmt, 3, presence_map);
2152
2153 if (presence == svn_wc__db_status_base_deleted)
2154 {
2155 keep_working = FALSE;
2156 no_delete_wc = TRUE;
2157 }
2158 else
2159 {
2160 keep_working = TRUE;
2161 }
2162 }
2163 else
2164 keep_working = FALSE;
2165 SVN_ERR(svn_sqlite__reset(stmt));
2166
2167 if (keep_as_working && op_depth == 0)
2168 {
2169 if (status == svn_wc__db_status_normal
2170 || status == svn_wc__db_status_incomplete)
2171 {
2172 SVN_ERR(svn_wc__db_op_make_copy_internal(wcroot, local_relpath, TRUE,
2173 NULL, NULL,
2174 scratch_pool));
2175 }
2176 keep_working = TRUE;
2177 }
2178
2179 /* Step 1: Create workqueue operations to remove files and dirs in the
2180 local-wc */
2181 if (!keep_working && !no_delete_wc)
2182 {
2183 svn_skel_t *work_item;
2184 const char *local_abspath;
2185
2186 local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
2187 scratch_pool);
2188 if (wrk_kind == svn_node_dir)
2189 {
2190 apr_pool_t *iterpool;
2191 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2192 STMT_SELECT_WORKING_PRESENT));
2193 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2194
2195 iterpool = svn_pool_create(scratch_pool);
2196
2197 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2198
2199 while (have_row)
2200 {
2201 const char *node_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2202 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 1,
2203 kind_map);
2204 const char *node_abspath;
2205 svn_error_t *err;
2206
2207 svn_pool_clear(iterpool);
2208
2209 node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
2210 iterpool);
2211
2212 if (node_kind == svn_node_dir)
2213 err = svn_wc__wq_build_dir_remove(&work_item,
2214 db, wcroot->abspath,
2215 node_abspath, FALSE,
2216 iterpool, iterpool);
2217 else
2218 err = svn_wc__wq_build_file_remove(&work_item,
2219 db,
2220 wcroot->abspath,
2221 node_abspath,
2222 iterpool, iterpool);
2223
2224 if (!err)
2225 err = add_work_items(wcroot->sdb, work_item, iterpool);
2226 if (err)
2227 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2228
2229 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2230 }
2231
2232 SVN_ERR(svn_sqlite__reset(stmt));
2233
2234 SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
2235 db, wcroot->abspath,
2236 local_abspath, FALSE,
2237 scratch_pool, iterpool));
2238 svn_pool_destroy(iterpool);
2239 }
2240 else
2241 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
2242 db, wcroot->abspath,
2243 local_abspath,
2244 scratch_pool, scratch_pool));
2245
2246 SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
2247 }
2248
2249 /* Step 2: Delete ACTUAL nodes */
2250 if (! keep_working)
2251 {
2252 /* There won't be a record in NODE left for this node, so we want
2253 to remove *all* ACTUAL nodes, including ACTUAL ONLY. */
2254 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2255 STMT_DELETE_ACTUAL_NODE_RECURSIVE));
2256 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2257 SVN_ERR(svn_sqlite__step_done(stmt));
2258 }
2259 else if (! keep_as_working)
2260 {
2261 /* Delete only the ACTUAL nodes that apply to a delete of a BASE node */
2262 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2263 STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE));
2264 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2265 SVN_ERR(svn_sqlite__step_done(stmt));
2266 }
2267 /* Else: Everything has been turned into a copy, so we want to keep all
2268 ACTUAL_NODE records */
2269
2270 /* Step 3: Delete WORKING nodes */
2271 if (!keep_working)
2272 {
2273 apr_pool_t *iterpool;
2274
2275 /* When deleting everything in working we should break moves from
2276 here and to here.
2277 */
2278 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2279 STMT_SELECT_MOVED_OUTSIDE));
2280 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2281 local_relpath,
2282 relpath_depth(local_relpath)));
2283 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2284 iterpool = svn_pool_create(scratch_pool);
2285 while (have_row)
2286 {
2287 const char *moved_to_relpath;
2288 svn_error_t *err;
2289
2290 svn_pool_clear(iterpool);
2291 moved_to_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
2292 err = clear_moved_here(wcroot, moved_to_relpath, iterpool);
2293 if (err)
2294 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2295 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2296 }
2297 svn_pool_destroy(iterpool);
2298 SVN_ERR(svn_sqlite__reset(stmt));
2299 }
2300 else
2301 {
2302 /* We are keeping things that are in WORKING, but we should still
2303 break moves of things in BASE. (Mixed revisions make it
2304 impossible to guarantee that we can keep everything moved) */
2305
2306 apr_pool_t *iterpool;
2307
2308 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2309 STMT_SELECT_MOVED_DESCENDANTS_SRC));
2310 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2311 local_relpath, 0));
2312 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2313 iterpool = svn_pool_create(scratch_pool);
2314 while (have_row)
2315 {
2316 int delete_op_depth = svn_sqlite__column_int(stmt, 0);
2317 const char *src_relpath;
2318 const char *dst_relpath;
2319 svn_error_t *err;
2320
2321 svn_pool_clear(iterpool);
2322
2323 src_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
2324 dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool);
2325
2326 err = svn_wc__db_op_break_move_internal(wcroot, src_relpath,
2327 delete_op_depth,
2328 dst_relpath,
2329 NULL,
2330 iterpool);
2331
2332 if (err)
2333 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2334
2335 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2336 }
2337 svn_pool_destroy(iterpool);
2338 SVN_ERR(svn_sqlite__reset(stmt));
2339 }
2340 if (keep_working)
2341 {
2342 SVN_ERR(svn_sqlite__get_statement(
2343 &stmt, wcroot->sdb,
2344 STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE));
2345 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
2346 SVN_ERR(svn_sqlite__step_done(stmt));
2347 }
2348 else
2349 {
2350 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2351 STMT_DELETE_WORKING_RECURSIVE));
2352 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2353 SVN_ERR(svn_sqlite__step_done(stmt));
2354 }
2355
2356 /* Step 4: Delete the BASE node descendants */
2357 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2358 STMT_DELETE_BASE_RECURSIVE));
2359 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2360 SVN_ERR(svn_sqlite__step_done(stmt));
2361
2362 SVN_ERR(db_retract_parent_delete(wcroot, local_relpath, 0, scratch_pool));
2363
2364 if (mark_not_present || mark_excluded)
2365 {
2366 struct insert_base_baton_t ibb;
2367 svn_boolean_t no_marker = FALSE;
2368
2369 if (file_external)
2370 {
2371 const char *parent_local_relpath;
2372 const char *name;
2373 svn_error_t *err;
2374
2375 /* For file externals we only want to place a not present marker
2376 if there is a BASE parent */
2377
2378 svn_relpath_split(&parent_local_relpath, &name, local_relpath,
2379 scratch_pool);
2380
2381 err = svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
2382 &repos_relpath, &repos_id,
2383 NULL, NULL, NULL, NULL, NULL,
2384 NULL, NULL, NULL, NULL, NULL,
2385 wcroot, parent_local_relpath,
2386 scratch_pool, scratch_pool);
2387
2388 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2389 return svn_error_trace(err);
2390 else if (err)
2391 {
2392 svn_error_clear(err);
2393 no_marker = TRUE;
2394 }
2395 else
2396 {
2397 /* Replace the repos_relpath with something more expected than
2398 the unrelated old file external repository relpath, which
2399 one day may come from a different repository */
2400 repos_relpath = svn_relpath_join(repos_relpath, name, scratch_pool);
2401 }
2402 }
2403
2404 if (!no_marker)
2405 {
2406 blank_ibb(&ibb);
2407
2408 ibb.repos_id = repos_id;
2409 ibb.status = mark_excluded ? svn_wc__db_status_excluded
2410 : svn_wc__db_status_not_present;
2411 ibb.kind = kind;
2412 ibb.repos_relpath = repos_relpath;
2413 ibb.revision = SVN_IS_VALID_REVNUM(marker_revision)
2414 ? marker_revision
2415 : revision;
2416
2417 /* Depending upon KIND, any of these might get used. */
2418 ibb.children = NULL;
2419 ibb.depth = svn_depth_unknown;
2420 ibb.checksum = NULL;
2421 ibb.target = NULL;
2422
2423 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
2424 }
2425 }
2426
2427 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2428 if (conflict)
2429 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
2430 conflict, scratch_pool));
2431
2432 return SVN_NO_ERROR;
2433 }
2434
2435
2436 svn_error_t *
svn_wc__db_base_remove(svn_wc__db_t * db,const char * local_abspath,svn_boolean_t keep_as_working,svn_boolean_t mark_not_present,svn_boolean_t mark_excluded,svn_revnum_t marker_revision,svn_skel_t * conflict,svn_skel_t * work_items,apr_pool_t * scratch_pool)2437 svn_wc__db_base_remove(svn_wc__db_t *db,
2438 const char *local_abspath,
2439 svn_boolean_t keep_as_working,
2440 svn_boolean_t mark_not_present,
2441 svn_boolean_t mark_excluded,
2442 svn_revnum_t marker_revision,
2443 svn_skel_t *conflict,
2444 svn_skel_t *work_items,
2445 apr_pool_t *scratch_pool)
2446 {
2447 svn_wc__db_wcroot_t *wcroot;
2448 const char *local_relpath;
2449
2450 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2451
2452 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2453 local_abspath, scratch_pool, scratch_pool));
2454 VERIFY_USABLE_WCROOT(wcroot);
2455
2456 SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath,
2457 db, keep_as_working,
2458 mark_not_present, mark_excluded,
2459 marker_revision,
2460 conflict, work_items, scratch_pool),
2461 wcroot);
2462
2463 /* If this used to be a directory we should remove children so pass
2464 * depth infinity. */
2465 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2466 scratch_pool));
2467
2468 return SVN_NO_ERROR;
2469 }
2470
2471
2472 svn_error_t *
svn_wc__db_base_get_info_internal(svn_wc__db_status_t * status,svn_node_kind_t * kind,svn_revnum_t * revision,const char ** repos_relpath,apr_int64_t * repos_id,svn_revnum_t * changed_rev,apr_time_t * changed_date,const char ** changed_author,svn_depth_t * depth,const svn_checksum_t ** checksum,const char ** target,svn_wc__db_lock_t ** lock,svn_boolean_t * had_props,apr_hash_t ** props,svn_boolean_t * update_root,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2473 svn_wc__db_base_get_info_internal(svn_wc__db_status_t *status,
2474 svn_node_kind_t *kind,
2475 svn_revnum_t *revision,
2476 const char **repos_relpath,
2477 apr_int64_t *repos_id,
2478 svn_revnum_t *changed_rev,
2479 apr_time_t *changed_date,
2480 const char **changed_author,
2481 svn_depth_t *depth,
2482 const svn_checksum_t **checksum,
2483 const char **target,
2484 svn_wc__db_lock_t **lock,
2485 svn_boolean_t *had_props,
2486 apr_hash_t **props,
2487 svn_boolean_t *update_root,
2488 svn_wc__db_wcroot_t *wcroot,
2489 const char *local_relpath,
2490 apr_pool_t *result_pool,
2491 apr_pool_t *scratch_pool)
2492 {
2493 svn_sqlite__stmt_t *stmt;
2494 svn_boolean_t have_row;
2495 svn_error_t *err = SVN_NO_ERROR;
2496
2497 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2498 lock ? STMT_SELECT_BASE_NODE_WITH_LOCK
2499 : STMT_SELECT_BASE_NODE));
2500 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2501 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2502
2503 if (have_row)
2504 {
2505 svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2506 presence_map);
2507 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2508
2509 if (kind)
2510 {
2511 *kind = node_kind;
2512 }
2513 if (status)
2514 {
2515 *status = node_status;
2516 }
2517 repos_location_from_columns(repos_id, revision, repos_relpath,
2518 stmt, 0, 4, 1, result_pool);
2519 SVN_ERR_ASSERT(!repos_id || *repos_id != INVALID_REPOS_ID);
2520 SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
2521 if (lock)
2522 {
2523 *lock = lock_from_columns(stmt, 15, 16, 17, 18, result_pool);
2524 }
2525 if (changed_rev)
2526 {
2527 *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2528 }
2529 if (changed_date)
2530 {
2531 *changed_date = svn_sqlite__column_int64(stmt, 8);
2532 }
2533 if (changed_author)
2534 {
2535 /* Result may be NULL. */
2536 *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2537 }
2538 if (depth)
2539 {
2540 if (node_kind != svn_node_dir)
2541 {
2542 *depth = svn_depth_unknown;
2543 }
2544 else
2545 {
2546 *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2547 svn_depth_unknown);
2548 }
2549 }
2550 if (checksum)
2551 {
2552 if (node_kind != svn_node_file)
2553 {
2554 *checksum = NULL;
2555 }
2556 else
2557 {
2558 err = svn_sqlite__column_checksum(checksum, stmt, 5,
2559 result_pool);
2560 if (err != NULL)
2561 err = svn_error_createf(
2562 err->apr_err, err,
2563 _("The node '%s' has a corrupt checksum value."),
2564 path_for_error_message(wcroot, local_relpath,
2565 scratch_pool));
2566 }
2567 }
2568 if (target)
2569 {
2570 if (node_kind != svn_node_symlink)
2571 *target = NULL;
2572 else
2573 *target = svn_sqlite__column_text(stmt, 11, result_pool);
2574 }
2575 if (had_props)
2576 {
2577 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2578 }
2579 if (props)
2580 {
2581 if (node_status == svn_wc__db_status_normal
2582 || node_status == svn_wc__db_status_incomplete)
2583 {
2584 SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2585 result_pool, scratch_pool));
2586 if (*props == NULL)
2587 *props = apr_hash_make(result_pool);
2588 }
2589 else
2590 {
2591 assert(svn_sqlite__column_is_null(stmt, 13));
2592 *props = NULL;
2593 }
2594 }
2595 if (update_root)
2596 {
2597 /* It's an update root iff it's a file external. */
2598 *update_root = svn_sqlite__column_boolean(stmt, 14);
2599 }
2600 }
2601 else
2602 {
2603 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2604 _("The node '%s' was not found."),
2605 path_for_error_message(wcroot, local_relpath,
2606 scratch_pool));
2607 }
2608
2609 /* Note: given the composition, no need to wrap for tracing. */
2610 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2611 }
2612
2613
2614 svn_error_t *
svn_wc__db_base_get_info(svn_wc__db_status_t * status,svn_node_kind_t * kind,svn_revnum_t * revision,const char ** repos_relpath,const char ** repos_root_url,const char ** repos_uuid,svn_revnum_t * changed_rev,apr_time_t * changed_date,const char ** changed_author,svn_depth_t * depth,const svn_checksum_t ** checksum,const char ** target,svn_wc__db_lock_t ** lock,svn_boolean_t * had_props,apr_hash_t ** props,svn_boolean_t * update_root,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2615 svn_wc__db_base_get_info(svn_wc__db_status_t *status,
2616 svn_node_kind_t *kind,
2617 svn_revnum_t *revision,
2618 const char **repos_relpath,
2619 const char **repos_root_url,
2620 const char **repos_uuid,
2621 svn_revnum_t *changed_rev,
2622 apr_time_t *changed_date,
2623 const char **changed_author,
2624 svn_depth_t *depth,
2625 const svn_checksum_t **checksum,
2626 const char **target,
2627 svn_wc__db_lock_t **lock,
2628 svn_boolean_t *had_props,
2629 apr_hash_t **props,
2630 svn_boolean_t *update_root,
2631 svn_wc__db_t *db,
2632 const char *local_abspath,
2633 apr_pool_t *result_pool,
2634 apr_pool_t *scratch_pool)
2635 {
2636 svn_wc__db_wcroot_t *wcroot;
2637 const char *local_relpath;
2638 apr_int64_t repos_id;
2639
2640 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2641
2642 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2643 local_abspath, scratch_pool, scratch_pool));
2644 VERIFY_USABLE_WCROOT(wcroot);
2645
2646 SVN_WC__DB_WITH_TXN4(
2647 svn_wc__db_base_get_info_internal(status, kind, revision,
2648 repos_relpath, &repos_id,
2649 changed_rev, changed_date,
2650 changed_author, depth,
2651 checksum, target, lock,
2652 had_props, props, update_root,
2653 wcroot, local_relpath,
2654 result_pool, scratch_pool),
2655 svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
2656 wcroot, repos_id, result_pool),
2657 SVN_NO_ERROR,
2658 SVN_NO_ERROR,
2659 wcroot);
2660 SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
2661
2662 return SVN_NO_ERROR;
2663 }
2664
2665 /* The implementation of svn_wc__db_base_get_children_info */
2666 static svn_error_t *
base_get_children_info(apr_hash_t ** nodes,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_boolean_t obtain_locks,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2667 base_get_children_info(apr_hash_t **nodes,
2668 svn_wc__db_wcroot_t *wcroot,
2669 const char *local_relpath,
2670 svn_boolean_t obtain_locks,
2671 apr_pool_t *result_pool,
2672 apr_pool_t *scratch_pool)
2673 {
2674 svn_sqlite__stmt_t *stmt;
2675 svn_boolean_t have_row;
2676 apr_int64_t last_repos_id = INVALID_REPOS_ID;
2677 const char *last_repos_root_url = NULL;
2678
2679 *nodes = apr_hash_make(result_pool);
2680
2681 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2682 obtain_locks
2683 ? STMT_SELECT_BASE_CHILDREN_INFO_LOCK
2684 : STMT_SELECT_BASE_CHILDREN_INFO));
2685 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2686
2687 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2688
2689 while (have_row)
2690 {
2691 struct svn_wc__db_base_info_t *info;
2692 apr_int64_t repos_id;
2693 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2694 const char *name = svn_relpath_basename(child_relpath, result_pool);
2695
2696 info = apr_pcalloc(result_pool, sizeof(*info));
2697
2698 repos_id = svn_sqlite__column_int64(stmt, 1);
2699 info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
2700 info->status = svn_sqlite__column_token(stmt, 3, presence_map);
2701 info->kind = svn_sqlite__column_token(stmt, 4, kind_map);
2702 info->revnum = svn_sqlite__column_revnum(stmt, 5);
2703
2704 info->depth = svn_sqlite__column_token_null(stmt, 6, depth_map,
2705 svn_depth_unknown);
2706
2707 info->update_root = svn_sqlite__column_boolean(stmt, 7);
2708
2709 if (obtain_locks)
2710 info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool);
2711
2712 if (repos_id != last_repos_id)
2713 {
2714 svn_error_t *err;
2715
2716 err = svn_wc__db_fetch_repos_info(&last_repos_root_url, NULL,
2717 wcroot, repos_id,
2718 result_pool);
2719
2720 if (err)
2721 return svn_error_trace(
2722 svn_error_compose_create(err,
2723 svn_sqlite__reset(stmt)));
2724
2725 last_repos_id = repos_id;
2726 }
2727
2728 info->repos_root_url = last_repos_root_url;
2729
2730 svn_hash_sets(*nodes, name, info);
2731
2732 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2733 }
2734
2735 SVN_ERR(svn_sqlite__reset(stmt));
2736
2737 return SVN_NO_ERROR;
2738 }
2739
2740 svn_error_t *
svn_wc__db_base_get_children_info(apr_hash_t ** nodes,svn_wc__db_t * db,const char * dir_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2741 svn_wc__db_base_get_children_info(apr_hash_t **nodes,
2742 svn_wc__db_t *db,
2743 const char *dir_abspath,
2744 apr_pool_t *result_pool,
2745 apr_pool_t *scratch_pool)
2746 {
2747 svn_wc__db_wcroot_t *wcroot;
2748 const char *local_relpath;
2749
2750 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
2751
2752 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2753 dir_abspath, scratch_pool, scratch_pool));
2754 VERIFY_USABLE_WCROOT(wcroot);
2755
2756 return svn_error_trace(base_get_children_info(nodes,
2757 wcroot,
2758 local_relpath,
2759 TRUE /* obtain_locks */,
2760 result_pool,
2761 scratch_pool));
2762 }
2763
2764
2765 svn_error_t *
svn_wc__db_base_get_props(apr_hash_t ** props,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2766 svn_wc__db_base_get_props(apr_hash_t **props,
2767 svn_wc__db_t *db,
2768 const char *local_abspath,
2769 apr_pool_t *result_pool,
2770 apr_pool_t *scratch_pool)
2771 {
2772 svn_wc__db_status_t presence;
2773
2774 SVN_ERR(svn_wc__db_base_get_info(&presence, NULL, NULL, NULL, NULL,
2775 NULL, NULL, NULL, NULL, NULL,
2776 NULL, NULL, NULL, NULL, props, NULL,
2777 db, local_abspath,
2778 result_pool, scratch_pool));
2779 if (presence != svn_wc__db_status_normal
2780 && presence != svn_wc__db_status_incomplete)
2781 {
2782 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2783 _("The node '%s' has a BASE status that"
2784 " has no properties."),
2785 svn_dirent_local_style(local_abspath,
2786 scratch_pool));
2787 }
2788
2789 return SVN_NO_ERROR;
2790 }
2791
2792
2793 svn_error_t *
svn_wc__db_base_get_children(const apr_array_header_t ** children,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2794 svn_wc__db_base_get_children(const apr_array_header_t **children,
2795 svn_wc__db_t *db,
2796 const char *local_abspath,
2797 apr_pool_t *result_pool,
2798 apr_pool_t *scratch_pool)
2799 {
2800 svn_wc__db_wcroot_t *wcroot;
2801 const char *local_relpath;
2802
2803 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2804
2805 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2806 local_abspath,
2807 scratch_pool, scratch_pool));
2808 VERIFY_USABLE_WCROOT(wcroot);
2809
2810 return svn_error_trace(
2811 gather_children(children, wcroot, local_relpath,
2812 STMT_SELECT_OP_DEPTH_CHILDREN, 0,
2813 result_pool, scratch_pool));
2814 }
2815
2816
2817 svn_error_t *
svn_wc__db_base_set_dav_cache(svn_wc__db_t * db,const char * local_abspath,const apr_hash_t * props,apr_pool_t * scratch_pool)2818 svn_wc__db_base_set_dav_cache(svn_wc__db_t *db,
2819 const char *local_abspath,
2820 const apr_hash_t *props,
2821 apr_pool_t *scratch_pool)
2822 {
2823 svn_wc__db_wcroot_t *wcroot;
2824 const char *local_relpath;
2825 svn_sqlite__stmt_t *stmt;
2826 int affected_rows;
2827
2828 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2829
2830 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2831 local_abspath, scratch_pool, scratch_pool));
2832 VERIFY_USABLE_WCROOT(wcroot);
2833
2834 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2835 STMT_UPDATE_BASE_NODE_DAV_CACHE));
2836 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2837 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
2838
2839 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2840
2841 if (affected_rows != 1)
2842 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2843 _("The node '%s' was not found."),
2844 svn_dirent_local_style(local_abspath,
2845 scratch_pool));
2846
2847 return SVN_NO_ERROR;
2848 }
2849
2850
2851 svn_error_t *
svn_wc__db_base_get_dav_cache(apr_hash_t ** props,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2852 svn_wc__db_base_get_dav_cache(apr_hash_t **props,
2853 svn_wc__db_t *db,
2854 const char *local_abspath,
2855 apr_pool_t *result_pool,
2856 apr_pool_t *scratch_pool)
2857 {
2858 svn_wc__db_wcroot_t *wcroot;
2859 const char *local_relpath;
2860 svn_sqlite__stmt_t *stmt;
2861 svn_boolean_t have_row;
2862
2863 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2864
2865 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2866 local_abspath, scratch_pool, scratch_pool));
2867 VERIFY_USABLE_WCROOT(wcroot);
2868
2869 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2870 STMT_SELECT_BASE_DAV_CACHE));
2871
2872 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2873 if (!have_row)
2874 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
2875 svn_sqlite__reset(stmt),
2876 _("The node '%s' was not found."),
2877 svn_dirent_local_style(local_abspath,
2878 scratch_pool));
2879
2880 SVN_ERR(svn_sqlite__column_properties(props, stmt, 0, result_pool,
2881 scratch_pool));
2882 return svn_error_trace(svn_sqlite__reset(stmt));
2883 }
2884
2885
2886 svn_error_t *
svn_wc__db_base_clear_dav_cache_recursive(svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)2887 svn_wc__db_base_clear_dav_cache_recursive(svn_wc__db_t *db,
2888 const char *local_abspath,
2889 apr_pool_t *scratch_pool)
2890 {
2891 svn_wc__db_wcroot_t *wcroot;
2892 const char *local_relpath;
2893 svn_sqlite__stmt_t *stmt;
2894
2895 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2896 db, local_abspath,
2897 scratch_pool, scratch_pool));
2898 VERIFY_USABLE_WCROOT(wcroot);
2899
2900 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2901 STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE));
2902 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2903
2904 SVN_ERR(svn_sqlite__step_done(stmt));
2905
2906 return SVN_NO_ERROR;
2907 }
2908
2909
2910 svn_error_t *
svn_wc__db_depth_get_info(svn_wc__db_status_t * status,svn_node_kind_t * kind,svn_revnum_t * revision,const char ** repos_relpath,apr_int64_t * repos_id,svn_revnum_t * changed_rev,apr_time_t * changed_date,const char ** changed_author,svn_depth_t * depth,const svn_checksum_t ** checksum,const char ** target,svn_boolean_t * had_props,apr_hash_t ** props,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int op_depth,apr_pool_t * result_pool,apr_pool_t * scratch_pool)2911 svn_wc__db_depth_get_info(svn_wc__db_status_t *status,
2912 svn_node_kind_t *kind,
2913 svn_revnum_t *revision,
2914 const char **repos_relpath,
2915 apr_int64_t *repos_id,
2916 svn_revnum_t *changed_rev,
2917 apr_time_t *changed_date,
2918 const char **changed_author,
2919 svn_depth_t *depth,
2920 const svn_checksum_t **checksum,
2921 const char **target,
2922 svn_boolean_t *had_props,
2923 apr_hash_t **props,
2924 svn_wc__db_wcroot_t *wcroot,
2925 const char *local_relpath,
2926 int op_depth,
2927 apr_pool_t *result_pool,
2928 apr_pool_t *scratch_pool)
2929 {
2930 svn_sqlite__stmt_t *stmt;
2931 svn_boolean_t have_row;
2932 svn_error_t *err = SVN_NO_ERROR;
2933
2934 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2935 STMT_SELECT_DEPTH_NODE));
2936 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
2937 wcroot->wc_id, local_relpath, op_depth));
2938 SVN_ERR(svn_sqlite__step(&have_row, stmt));
2939
2940 if (have_row)
2941 {
2942 svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2943 presence_map);
2944 svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2945
2946 if (kind)
2947 {
2948 *kind = node_kind;
2949 }
2950 if (status)
2951 {
2952 *status = node_status;
2953
2954 if (op_depth > 0)
2955 SVN_ERR(convert_to_working_status(status, *status));
2956 }
2957 repos_location_from_columns(repos_id, revision, repos_relpath,
2958 stmt, 0, 4, 1, result_pool);
2959
2960 if (changed_rev)
2961 {
2962 *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2963 }
2964 if (changed_date)
2965 {
2966 *changed_date = svn_sqlite__column_int64(stmt, 8);
2967 }
2968 if (changed_author)
2969 {
2970 /* Result may be NULL. */
2971 *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2972 }
2973 if (depth)
2974 {
2975 if (node_kind != svn_node_dir)
2976 {
2977 *depth = svn_depth_unknown;
2978 }
2979 else
2980 {
2981 *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2982 svn_depth_unknown);
2983 }
2984 }
2985 if (checksum)
2986 {
2987 if (node_kind != svn_node_file)
2988 {
2989 *checksum = NULL;
2990 }
2991 else
2992 {
2993 err = svn_sqlite__column_checksum(checksum, stmt, 5,
2994 result_pool);
2995 if (err != NULL)
2996 err = svn_error_createf(
2997 err->apr_err, err,
2998 _("The node '%s' has a corrupt checksum value."),
2999 path_for_error_message(wcroot, local_relpath,
3000 scratch_pool));
3001 }
3002 }
3003 if (target)
3004 {
3005 if (node_kind != svn_node_symlink)
3006 *target = NULL;
3007 else
3008 *target = svn_sqlite__column_text(stmt, 11, result_pool);
3009 }
3010 if (had_props)
3011 {
3012 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 12);
3013 }
3014 if (props)
3015 {
3016 if (node_status == svn_wc__db_status_normal
3017 || node_status == svn_wc__db_status_incomplete)
3018 {
3019 SVN_ERR(svn_sqlite__column_properties(props, stmt, 12,
3020 result_pool, scratch_pool));
3021 if (*props == NULL)
3022 *props = apr_hash_make(result_pool);
3023 }
3024 else
3025 {
3026 assert(svn_sqlite__column_is_null(stmt, 12));
3027 *props = NULL;
3028 }
3029 }
3030 }
3031 else
3032 {
3033 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3034 _("The node '%s' was not found."),
3035 path_for_error_message(wcroot, local_relpath,
3036 scratch_pool));
3037 }
3038
3039 /* Note: given the composition, no need to wrap for tracing. */
3040 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
3041 }
3042
3043 /* A callback which supplies WCROOTs and LOCAL_RELPATHs. */
3044 typedef svn_error_t *(*svn_wc__db_txn_callback_t)(void *baton,
3045 svn_wc__db_wcroot_t *wcroot,
3046 const char *local_relpath,
3047 apr_pool_t *scratch_pool);
3048
3049 /* Baton for passing args to with_triggers(). */
3050 struct with_triggers_baton_t {
3051 int create_trigger;
3052 int drop_trigger;
3053 svn_wc__db_txn_callback_t cb_func;
3054 void *cb_baton;
3055 };
3056
3057 /* Helper for creating SQLite triggers, running the main transaction
3058 callback, and then dropping the triggers. It guarantees that the
3059 triggers will not survive the transaction. This could be used for
3060 any general prefix/postscript statements where the postscript
3061 *must* be executed if the transaction completes.
3062
3063 Implements svn_wc__db_txn_callback_t. */
3064 static svn_error_t *
with_triggers(void * baton,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)3065 with_triggers(void *baton,
3066 svn_wc__db_wcroot_t *wcroot,
3067 const char *local_relpath,
3068 apr_pool_t *scratch_pool)
3069 {
3070 struct with_triggers_baton_t *b = baton;
3071 svn_error_t *err1;
3072 svn_error_t *err2;
3073
3074 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, b->create_trigger));
3075
3076 err1 = b->cb_func(b->cb_baton, wcroot, local_relpath, scratch_pool);
3077
3078 err2 = svn_sqlite__exec_statements(wcroot->sdb, b->drop_trigger);
3079
3080 return svn_error_trace(svn_error_compose_create(err1, err2));
3081 }
3082
3083
3084 /* Prototype for the "work callback" used by with_finalization(). */
3085 typedef svn_error_t * (*work_callback_t)(
3086 void *baton,
3087 svn_wc__db_wcroot_t *wcroot,
3088 svn_cancel_func_t cancel_func,
3089 void *cancel_baton,
3090 svn_wc_notify_func2_t notify_func,
3091 void *notify_baton,
3092 apr_pool_t *scratch_pool);
3093
3094 /* Utility function to provide several features, with a guaranteed
3095 finalization (ie. to drop temporary tables).
3096
3097 1) for WCROOT and LOCAL_RELPATH, run TXN_CB(TXN_BATON) within a
3098 sqlite transaction
3099 2) if (1) is successful and a NOTIFY_FUNC is provided, then run
3100 the "work" step: WORK_CB(WORK_BATON).
3101 3) execute FINALIZE_STMT_IDX no matter what errors may be thrown
3102 from the above two steps.
3103
3104 CANCEL_FUNC, CANCEL_BATON, NOTIFY_FUNC and NOTIFY_BATON are their
3105 typical values. These are passed to the work callback, which typically
3106 provides notification about the work done by TXN_CB. */
3107 static svn_error_t *
with_finalization(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_wc__db_txn_callback_t txn_cb,void * txn_baton,work_callback_t work_cb,void * work_baton,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,int finalize_stmt_idx,apr_pool_t * scratch_pool)3108 with_finalization(svn_wc__db_wcroot_t *wcroot,
3109 const char *local_relpath,
3110 svn_wc__db_txn_callback_t txn_cb,
3111 void *txn_baton,
3112 work_callback_t work_cb,
3113 void *work_baton,
3114 svn_cancel_func_t cancel_func,
3115 void *cancel_baton,
3116 svn_wc_notify_func2_t notify_func,
3117 void *notify_baton,
3118 int finalize_stmt_idx,
3119 apr_pool_t *scratch_pool)
3120 {
3121 svn_error_t *err1;
3122 svn_error_t *err2;
3123
3124 err1 = svn_sqlite__begin_savepoint(wcroot->sdb);
3125 if (!err1)
3126 {
3127 err1 = txn_cb(txn_baton, wcroot, local_relpath, scratch_pool);
3128
3129 err1 = svn_sqlite__finish_savepoint(wcroot->sdb, err1);
3130 }
3131
3132 if (err1 == NULL && notify_func != NULL)
3133 {
3134 err2 = work_cb(work_baton, wcroot,
3135 cancel_func, cancel_baton,
3136 notify_func, notify_baton,
3137 scratch_pool);
3138 err1 = svn_error_compose_create(err1, err2);
3139 }
3140
3141 err2 = svn_sqlite__exec_statements(wcroot->sdb, finalize_stmt_idx);
3142
3143 return svn_error_trace(svn_error_compose_create(err1, err2));
3144 }
3145
3146
3147 /* Initialize the baton with appropriate "blank" values. This allows the
3148 insertion function to leave certain columns null. */
3149 static void
blank_ieb(insert_external_baton_t * ieb)3150 blank_ieb(insert_external_baton_t *ieb)
3151 {
3152 memset(ieb, 0, sizeof(*ieb));
3153 ieb->revision = SVN_INVALID_REVNUM;
3154 ieb->changed_rev = SVN_INVALID_REVNUM;
3155 ieb->repos_id = INVALID_REPOS_ID;
3156
3157 ieb->recorded_peg_revision = SVN_INVALID_REVNUM;
3158 ieb->recorded_revision = SVN_INVALID_REVNUM;
3159 }
3160
3161 /* Insert the externals row represented by (insert_external_baton_t *) BATON.
3162 *
3163 * Implements svn_wc__db_txn_callback_t. */
3164 static svn_error_t *
insert_external_node(const insert_external_baton_t * ieb,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)3165 insert_external_node(const insert_external_baton_t *ieb,
3166 svn_wc__db_wcroot_t *wcroot,
3167 const char *local_relpath,
3168 apr_pool_t *scratch_pool)
3169 {
3170 svn_wc__db_status_t status;
3171 svn_error_t *err;
3172 svn_boolean_t update_root;
3173 apr_int64_t repos_id;
3174 svn_sqlite__stmt_t *stmt;
3175
3176 if (ieb->repos_id != INVALID_REPOS_ID)
3177 repos_id = ieb->repos_id;
3178 else
3179 SVN_ERR(create_repos_id(&repos_id, ieb->repos_root_url, ieb->repos_uuid,
3180 wcroot->sdb, scratch_pool));
3181
3182 /* And there must be no existing BASE node or it must be a file external */
3183 err = svn_wc__db_base_get_info_internal(&status, NULL, NULL, NULL, NULL,
3184 NULL, NULL, NULL, NULL, NULL,
3185 NULL, NULL, NULL, NULL, &update_root,
3186 wcroot, local_relpath,
3187 scratch_pool, scratch_pool);
3188 if (err)
3189 {
3190 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3191 return svn_error_trace(err);
3192
3193 svn_error_clear(err);
3194 }
3195 else if (status == svn_wc__db_status_normal && !update_root)
3196 return svn_error_create(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, NULL);
3197
3198 if (ieb->kind == svn_node_file
3199 || ieb->kind == svn_node_symlink)
3200 {
3201 struct insert_base_baton_t ibb;
3202
3203 blank_ibb(&ibb);
3204
3205 ibb.status = svn_wc__db_status_normal;
3206 ibb.kind = ieb->kind;
3207
3208 ibb.repos_id = repos_id;
3209 ibb.repos_relpath = ieb->repos_relpath;
3210 ibb.revision = ieb->revision;
3211
3212 ibb.props = ieb->props;
3213 ibb.iprops = ieb->iprops;
3214 ibb.changed_rev = ieb->changed_rev;
3215 ibb.changed_date = ieb->changed_date;
3216 ibb.changed_author = ieb->changed_author;
3217
3218 ibb.dav_cache = ieb->dav_cache;
3219
3220 ibb.checksum = ieb->checksum;
3221 ibb.target = ieb->target;
3222
3223 ibb.conflict = ieb->conflict;
3224
3225 ibb.update_actual_props = ieb->update_actual_props;
3226 ibb.new_actual_props = ieb->new_actual_props;
3227
3228 ibb.keep_recorded_info = ieb->keep_recorded_info;
3229
3230 ibb.work_items = ieb->work_items;
3231
3232 ibb.file_external = TRUE;
3233
3234 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
3235 }
3236 else
3237 SVN_ERR(add_work_items(wcroot->sdb, ieb->work_items, scratch_pool));
3238
3239 /* The externals table only support presence normal and excluded */
3240 SVN_ERR_ASSERT(ieb->presence == svn_wc__db_status_normal
3241 || ieb->presence == svn_wc__db_status_excluded);
3242
3243 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_EXTERNAL));
3244
3245 SVN_ERR(svn_sqlite__bindf(stmt, "issttsis",
3246 wcroot->wc_id,
3247 local_relpath,
3248 svn_relpath_dirname(local_relpath,
3249 scratch_pool),
3250 presence_map, ieb->presence,
3251 kind_map, ieb->kind,
3252 ieb->record_ancestor_relpath,
3253 repos_id,
3254 ieb->recorded_repos_relpath));
3255
3256 if (SVN_IS_VALID_REVNUM(ieb->recorded_peg_revision))
3257 SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, ieb->recorded_peg_revision));
3258
3259 if (SVN_IS_VALID_REVNUM(ieb->recorded_revision))
3260 SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, ieb->recorded_revision));
3261
3262 SVN_ERR(svn_sqlite__insert(NULL, stmt));
3263
3264 return SVN_NO_ERROR;
3265 }
3266
3267 svn_error_t *
svn_wc__db_external_add_file(svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t revision,const apr_hash_t * props,apr_array_header_t * iprops,svn_revnum_t changed_rev,apr_time_t changed_date,const char * changed_author,const svn_checksum_t * checksum,const apr_hash_t * dav_cache,const char * record_ancestor_abspath,const char * recorded_repos_relpath,svn_revnum_t recorded_peg_revision,svn_revnum_t recorded_revision,svn_boolean_t update_actual_props,apr_hash_t * new_actual_props,svn_boolean_t keep_recorded_info,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)3268 svn_wc__db_external_add_file(svn_wc__db_t *db,
3269 const char *local_abspath,
3270 const char *wri_abspath,
3271
3272 const char *repos_relpath,
3273 const char *repos_root_url,
3274 const char *repos_uuid,
3275 svn_revnum_t revision,
3276
3277 const apr_hash_t *props,
3278 apr_array_header_t *iprops,
3279
3280 svn_revnum_t changed_rev,
3281 apr_time_t changed_date,
3282 const char *changed_author,
3283
3284 const svn_checksum_t *checksum,
3285
3286 const apr_hash_t *dav_cache,
3287
3288 const char *record_ancestor_abspath,
3289 const char *recorded_repos_relpath,
3290 svn_revnum_t recorded_peg_revision,
3291 svn_revnum_t recorded_revision,
3292
3293 svn_boolean_t update_actual_props,
3294 apr_hash_t *new_actual_props,
3295
3296 svn_boolean_t keep_recorded_info,
3297 const svn_skel_t *conflict,
3298 const svn_skel_t *work_items,
3299 apr_pool_t *scratch_pool)
3300 {
3301 svn_wc__db_wcroot_t *wcroot;
3302 const char *local_relpath;
3303 insert_external_baton_t ieb;
3304
3305 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3306
3307 if (! wri_abspath)
3308 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3309
3310 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3311 wri_abspath, scratch_pool, scratch_pool));
3312 VERIFY_USABLE_WCROOT(wcroot);
3313
3314 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3315 record_ancestor_abspath));
3316
3317 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3318
3319 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3320
3321 blank_ieb(&ieb);
3322
3323 ieb.kind = svn_node_file;
3324 ieb.presence = svn_wc__db_status_normal;
3325
3326 ieb.repos_root_url = repos_root_url;
3327 ieb.repos_uuid = repos_uuid;
3328
3329 ieb.repos_relpath = repos_relpath;
3330 ieb.revision = revision;
3331
3332 ieb.props = props;
3333 ieb.iprops = iprops;
3334
3335 ieb.changed_rev = changed_rev;
3336 ieb.changed_date = changed_date;
3337 ieb.changed_author = changed_author;
3338
3339 ieb.checksum = checksum;
3340
3341 ieb.dav_cache = dav_cache;
3342
3343 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3344 wcroot->abspath,
3345 record_ancestor_abspath);
3346 ieb.recorded_repos_relpath = recorded_repos_relpath;
3347 ieb.recorded_peg_revision = recorded_peg_revision;
3348 ieb.recorded_revision = recorded_revision;
3349
3350 ieb.update_actual_props = update_actual_props;
3351 ieb.new_actual_props = new_actual_props;
3352
3353 ieb.keep_recorded_info = keep_recorded_info;
3354
3355 ieb.conflict = conflict;
3356 ieb.work_items = work_items;
3357
3358 SVN_WC__DB_WITH_TXN(
3359 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3360 wcroot);
3361
3362 return SVN_NO_ERROR;
3363 }
3364
3365 svn_error_t *
svn_wc__db_external_add_symlink(svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t revision,const apr_hash_t * props,svn_revnum_t changed_rev,apr_time_t changed_date,const char * changed_author,const char * target,const apr_hash_t * dav_cache,const char * record_ancestor_abspath,const char * recorded_repos_relpath,svn_revnum_t recorded_peg_revision,svn_revnum_t recorded_revision,svn_boolean_t update_actual_props,apr_hash_t * new_actual_props,svn_boolean_t keep_recorded_info,const svn_skel_t * work_items,apr_pool_t * scratch_pool)3366 svn_wc__db_external_add_symlink(svn_wc__db_t *db,
3367 const char *local_abspath,
3368 const char *wri_abspath,
3369 const char *repos_relpath,
3370 const char *repos_root_url,
3371 const char *repos_uuid,
3372 svn_revnum_t revision,
3373 const apr_hash_t *props,
3374 svn_revnum_t changed_rev,
3375 apr_time_t changed_date,
3376 const char *changed_author,
3377 const char *target,
3378 const apr_hash_t *dav_cache,
3379 const char *record_ancestor_abspath,
3380 const char *recorded_repos_relpath,
3381 svn_revnum_t recorded_peg_revision,
3382 svn_revnum_t recorded_revision,
3383 svn_boolean_t update_actual_props,
3384 apr_hash_t *new_actual_props,
3385 svn_boolean_t keep_recorded_info,
3386 const svn_skel_t *work_items,
3387 apr_pool_t *scratch_pool)
3388 {
3389 svn_wc__db_wcroot_t *wcroot;
3390 const char *local_relpath;
3391 insert_external_baton_t ieb;
3392
3393 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3394
3395 if (! wri_abspath)
3396 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3397
3398 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3399 wri_abspath, scratch_pool, scratch_pool));
3400 VERIFY_USABLE_WCROOT(wcroot);
3401
3402 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3403 record_ancestor_abspath));
3404
3405 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3406
3407 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3408
3409 blank_ieb(&ieb);
3410
3411 ieb.kind = svn_node_symlink;
3412 ieb.presence = svn_wc__db_status_normal;
3413
3414 ieb.repos_root_url = repos_root_url;
3415 ieb.repos_uuid = repos_uuid;
3416
3417 ieb.repos_relpath = repos_relpath;
3418 ieb.revision = revision;
3419
3420 ieb.props = props;
3421
3422 ieb.changed_rev = changed_rev;
3423 ieb.changed_date = changed_date;
3424 ieb.changed_author = changed_author;
3425
3426 ieb.target = target;
3427
3428 ieb.dav_cache = dav_cache;
3429
3430 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3431 wcroot->abspath,
3432 record_ancestor_abspath);
3433 ieb.recorded_repos_relpath = recorded_repos_relpath;
3434 ieb.recorded_peg_revision = recorded_peg_revision;
3435 ieb.recorded_revision = recorded_revision;
3436
3437 ieb.update_actual_props = update_actual_props;
3438 ieb.new_actual_props = new_actual_props;
3439
3440 ieb.keep_recorded_info = keep_recorded_info;
3441
3442 ieb.work_items = work_items;
3443
3444 SVN_WC__DB_WITH_TXN(
3445 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3446 wcroot);
3447
3448 return SVN_NO_ERROR;
3449 }
3450
3451 svn_error_t *
svn_wc__db_external_add_dir(svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,const char * repos_root_url,const char * repos_uuid,const char * record_ancestor_abspath,const char * recorded_repos_relpath,svn_revnum_t recorded_peg_revision,svn_revnum_t recorded_revision,const svn_skel_t * work_items,apr_pool_t * scratch_pool)3452 svn_wc__db_external_add_dir(svn_wc__db_t *db,
3453 const char *local_abspath,
3454 const char *wri_abspath,
3455 const char *repos_root_url,
3456 const char *repos_uuid,
3457 const char *record_ancestor_abspath,
3458 const char *recorded_repos_relpath,
3459 svn_revnum_t recorded_peg_revision,
3460 svn_revnum_t recorded_revision,
3461 const svn_skel_t *work_items,
3462 apr_pool_t *scratch_pool)
3463 {
3464 svn_wc__db_wcroot_t *wcroot;
3465 const char *local_relpath;
3466 insert_external_baton_t ieb;
3467
3468 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3469
3470 if (! wri_abspath)
3471 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3472
3473 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3474 wri_abspath, scratch_pool, scratch_pool));
3475 VERIFY_USABLE_WCROOT(wcroot);
3476
3477 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3478 record_ancestor_abspath));
3479
3480 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3481
3482 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3483
3484 blank_ieb(&ieb);
3485
3486 ieb.kind = svn_node_dir;
3487 ieb.presence = svn_wc__db_status_normal;
3488
3489 ieb.repos_root_url = repos_root_url;
3490 ieb.repos_uuid = repos_uuid;
3491
3492 ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3493 wcroot->abspath,
3494 record_ancestor_abspath);
3495 ieb.recorded_repos_relpath = recorded_repos_relpath;
3496 ieb.recorded_peg_revision = recorded_peg_revision;
3497 ieb.recorded_revision = recorded_revision;
3498
3499 ieb.work_items = work_items;
3500
3501 SVN_WC__DB_WITH_TXN(
3502 insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3503 wcroot);
3504
3505 return SVN_NO_ERROR;
3506 }
3507
3508 /* The body of svn_wc__db_external_remove(). */
3509 static svn_error_t *
db_external_remove(const svn_skel_t * work_items,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)3510 db_external_remove(const svn_skel_t *work_items,
3511 svn_wc__db_wcroot_t *wcroot,
3512 const char *local_relpath,
3513 apr_pool_t *scratch_pool)
3514 {
3515 svn_sqlite__stmt_t *stmt;
3516 int affected_rows;
3517
3518 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3519 STMT_DELETE_EXTERNAL));
3520 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3521 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
3522
3523 if (!affected_rows)
3524 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3525 _("The node '%s' is not an external."),
3526 path_for_error_message(wcroot, local_relpath,
3527 scratch_pool));
3528
3529 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
3530
3531 /* ### What about actual? */
3532 return SVN_NO_ERROR;
3533 }
3534
3535 svn_error_t *
svn_wc__db_external_remove(svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,const svn_skel_t * work_items,apr_pool_t * scratch_pool)3536 svn_wc__db_external_remove(svn_wc__db_t *db,
3537 const char *local_abspath,
3538 const char *wri_abspath,
3539 const svn_skel_t *work_items,
3540 apr_pool_t *scratch_pool)
3541 {
3542 svn_wc__db_wcroot_t *wcroot;
3543 const char *local_relpath;
3544
3545 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3546
3547 if (! wri_abspath)
3548 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3549
3550 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3551 wri_abspath, scratch_pool, scratch_pool));
3552 VERIFY_USABLE_WCROOT(wcroot);
3553
3554 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3555
3556 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3557
3558 SVN_WC__DB_WITH_TXN(db_external_remove(work_items, wcroot, local_relpath,
3559 scratch_pool),
3560 wcroot);
3561
3562 return SVN_NO_ERROR;
3563 }
3564
3565 svn_error_t *
svn_wc__db_external_read(svn_wc__db_status_t * status,svn_node_kind_t * kind,const char ** definining_abspath,const char ** repos_root_url,const char ** repos_uuid,const char ** recorded_repos_relpath,svn_revnum_t * recorded_peg_revision,svn_revnum_t * recorded_revision,svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3566 svn_wc__db_external_read(svn_wc__db_status_t *status,
3567 svn_node_kind_t *kind,
3568 const char **definining_abspath,
3569 const char **repos_root_url,
3570 const char **repos_uuid,
3571 const char **recorded_repos_relpath,
3572 svn_revnum_t *recorded_peg_revision,
3573 svn_revnum_t *recorded_revision,
3574 svn_wc__db_t *db,
3575 const char *local_abspath,
3576 const char *wri_abspath,
3577 apr_pool_t *result_pool,
3578 apr_pool_t *scratch_pool)
3579 {
3580 svn_wc__db_wcroot_t *wcroot;
3581 const char *local_relpath;
3582 svn_sqlite__stmt_t *stmt;
3583 svn_boolean_t have_info;
3584 svn_error_t *err = NULL;
3585 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3586
3587 if (! wri_abspath)
3588 wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3589
3590 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3591 wri_abspath, scratch_pool, scratch_pool));
3592 VERIFY_USABLE_WCROOT(wcroot);
3593
3594 SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3595
3596 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3597
3598 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3599 STMT_SELECT_EXTERNAL_INFO));
3600 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3601 SVN_ERR(svn_sqlite__step(&have_info, stmt));
3602
3603 if (have_info)
3604 {
3605 if (status)
3606 *status = svn_sqlite__column_token(stmt, 0, presence_map);
3607
3608 if (kind)
3609 *kind = svn_sqlite__column_token(stmt, 1, kind_map);
3610
3611 if (definining_abspath)
3612 {
3613 const char *record_relpath = svn_sqlite__column_text(stmt, 2, NULL);
3614
3615 *definining_abspath = svn_dirent_join(wcroot->abspath,
3616 record_relpath, result_pool);
3617 }
3618
3619 if (repos_root_url || repos_uuid)
3620 {
3621 apr_int64_t repos_id;
3622
3623 repos_id = svn_sqlite__column_int64(stmt, 3);
3624
3625 err = svn_error_compose_create(
3626 err,
3627 svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
3628 wcroot, repos_id,
3629 result_pool));
3630 }
3631
3632 if (recorded_repos_relpath)
3633 *recorded_repos_relpath = svn_sqlite__column_text(stmt, 4,
3634 result_pool);
3635
3636 if (recorded_peg_revision)
3637 *recorded_peg_revision = svn_sqlite__column_revnum(stmt, 5);
3638
3639 if (recorded_revision)
3640 *recorded_revision = svn_sqlite__column_revnum(stmt, 6);
3641 }
3642 else
3643 {
3644 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3645 _("The node '%s' is not an external."),
3646 svn_dirent_local_style(local_abspath,
3647 scratch_pool));
3648 }
3649
3650 return svn_error_trace(
3651 svn_error_compose_create(err, svn_sqlite__reset(stmt)));
3652 }
3653
3654 svn_error_t *
svn_wc__db_committable_externals_below(apr_array_header_t ** externals,svn_wc__db_t * db,const char * local_abspath,svn_boolean_t immediates_only,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3655 svn_wc__db_committable_externals_below(apr_array_header_t **externals,
3656 svn_wc__db_t *db,
3657 const char *local_abspath,
3658 svn_boolean_t immediates_only,
3659 apr_pool_t *result_pool,
3660 apr_pool_t *scratch_pool)
3661 {
3662 svn_wc__db_wcroot_t *wcroot;
3663 svn_sqlite__stmt_t *stmt;
3664 const char *local_relpath;
3665 svn_boolean_t have_row;
3666 svn_wc__committable_external_info_t *info;
3667 svn_node_kind_t db_kind;
3668 apr_array_header_t *result = NULL;
3669
3670 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3671
3672 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3673 local_abspath, scratch_pool, scratch_pool));
3674 VERIFY_USABLE_WCROOT(wcroot);
3675
3676 SVN_ERR(svn_sqlite__get_statement(
3677 &stmt, wcroot->sdb,
3678 immediates_only
3679 ? STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW
3680 : STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW));
3681
3682 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3683
3684 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3685
3686 if (have_row)
3687 result = apr_array_make(result_pool, 0,
3688 sizeof(svn_wc__committable_external_info_t *));
3689
3690 while (have_row)
3691 {
3692 info = apr_palloc(result_pool, sizeof(*info));
3693
3694 local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3695 info->local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
3696 result_pool);
3697
3698 db_kind = svn_sqlite__column_token(stmt, 1, kind_map);
3699 SVN_ERR_ASSERT(db_kind == svn_node_file || db_kind == svn_node_dir);
3700 info->kind = db_kind;
3701
3702 info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
3703 info->repos_root_url = svn_sqlite__column_text(stmt, 3, result_pool);
3704
3705 APR_ARRAY_PUSH(result, svn_wc__committable_external_info_t *) = info;
3706
3707 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3708 }
3709
3710 *externals = result;
3711 return svn_error_trace(svn_sqlite__reset(stmt));
3712 }
3713
3714 svn_error_t *
svn_wc__db_externals_defined_below(apr_hash_t ** externals,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3715 svn_wc__db_externals_defined_below(apr_hash_t **externals,
3716 svn_wc__db_t *db,
3717 const char *local_abspath,
3718 apr_pool_t *result_pool,
3719 apr_pool_t *scratch_pool)
3720 {
3721 svn_wc__db_wcroot_t *wcroot;
3722 svn_sqlite__stmt_t *stmt;
3723 const char *local_relpath;
3724 svn_boolean_t have_row;
3725
3726 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3727
3728 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3729 local_abspath, scratch_pool, scratch_pool));
3730 VERIFY_USABLE_WCROOT(wcroot);
3731
3732 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3733 STMT_SELECT_EXTERNALS_DEFINED));
3734
3735 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3736
3737 *externals = apr_hash_make(result_pool);
3738 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3739
3740 while (have_row)
3741 {
3742 const char *def_local_relpath;
3743
3744 local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3745 def_local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3746
3747 svn_hash_sets(*externals,
3748 svn_dirent_join(wcroot->abspath, local_relpath,
3749 result_pool),
3750 svn_dirent_join(wcroot->abspath, def_local_relpath,
3751 result_pool));
3752
3753 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3754 }
3755
3756 return svn_error_trace(svn_sqlite__reset(stmt));
3757 }
3758
3759 svn_error_t *
svn_wc__db_externals_gather_definitions(apr_hash_t ** externals,apr_hash_t ** depths,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3760 svn_wc__db_externals_gather_definitions(apr_hash_t **externals,
3761 apr_hash_t **depths,
3762 svn_wc__db_t *db,
3763 const char *local_abspath,
3764 apr_pool_t *result_pool,
3765 apr_pool_t *scratch_pool)
3766 {
3767 svn_wc__db_wcroot_t *wcroot;
3768 svn_sqlite__stmt_t *stmt;
3769 const char *local_relpath;
3770 svn_boolean_t have_row;
3771 svn_error_t *err = NULL;
3772 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3773
3774 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3775
3776 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3777 local_abspath, scratch_pool, iterpool));
3778 VERIFY_USABLE_WCROOT(wcroot);
3779
3780 *externals = apr_hash_make(result_pool);
3781 if (depths != NULL)
3782 *depths = apr_hash_make(result_pool);
3783
3784 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3785 STMT_SELECT_EXTERNAL_PROPERTIES));
3786
3787 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3788
3789 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3790
3791 while (have_row)
3792 {
3793 apr_hash_t *node_props;
3794 const char *external_value;
3795
3796 svn_pool_clear(iterpool);
3797 err = svn_sqlite__column_properties(&node_props, stmt, 0, iterpool,
3798 iterpool);
3799
3800 if (err)
3801 break;
3802
3803 external_value = svn_prop_get_value(node_props, SVN_PROP_EXTERNALS);
3804
3805 if (external_value)
3806 {
3807 const char *node_abspath;
3808 const char *node_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3809
3810 node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
3811 result_pool);
3812
3813 svn_hash_sets(*externals, node_abspath,
3814 apr_pstrdup(result_pool, external_value));
3815
3816 if (depths)
3817 {
3818 svn_depth_t depth
3819 = svn_sqlite__column_token_null(stmt, 2, depth_map,
3820 svn_depth_unknown);
3821
3822 svn_hash_sets(*depths, node_abspath,
3823 /* Use static string */
3824 svn_token__to_word(depth_map, depth));
3825 }
3826 }
3827
3828 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3829 }
3830
3831 svn_pool_destroy(iterpool);
3832
3833 return svn_error_trace(svn_error_compose_create(err,
3834 svn_sqlite__reset(stmt)));
3835 }
3836
3837 /* Copy the ACTUAL data for SRC_RELPATH and tweak it to refer to DST_RELPATH.
3838 The new ACTUAL data won't have any conflicts. */
3839 static svn_error_t *
copy_actual(svn_wc__db_wcroot_t * src_wcroot,const char * src_relpath,svn_wc__db_wcroot_t * dst_wcroot,const char * dst_relpath,apr_pool_t * scratch_pool)3840 copy_actual(svn_wc__db_wcroot_t *src_wcroot,
3841 const char *src_relpath,
3842 svn_wc__db_wcroot_t *dst_wcroot,
3843 const char *dst_relpath,
3844 apr_pool_t *scratch_pool)
3845 {
3846 svn_sqlite__stmt_t *stmt;
3847 svn_boolean_t have_row;
3848
3849 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
3850 STMT_SELECT_ACTUAL_NODE));
3851 SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
3852 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3853 if (have_row)
3854 {
3855 apr_size_t props_size;
3856 const char *changelist;
3857 const char *properties;
3858
3859 /* Skipping conflict data... */
3860 changelist = svn_sqlite__column_text(stmt, 0, scratch_pool);
3861 /* No need to parse the properties when simply copying. */
3862 properties = svn_sqlite__column_blob(stmt, 1, &props_size, scratch_pool);
3863
3864 if (changelist || properties)
3865 {
3866 SVN_ERR(svn_sqlite__reset(stmt));
3867
3868 SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
3869 STMT_INSERT_ACTUAL_NODE));
3870 SVN_ERR(svn_sqlite__bindf(stmt, "issbs",
3871 dst_wcroot->wc_id, dst_relpath,
3872 svn_relpath_dirname(dst_relpath, scratch_pool),
3873 properties, props_size, changelist));
3874 SVN_ERR(svn_sqlite__step(&have_row, stmt));
3875 }
3876 }
3877 SVN_ERR(svn_sqlite__reset(stmt));
3878
3879 return SVN_NO_ERROR;
3880 }
3881
3882 /* Helper for svn_wc__db_op_copy to handle copying from one db to
3883 another */
3884 static svn_error_t *
cross_db_copy(svn_wc__db_wcroot_t * src_wcroot,const char * src_relpath,svn_wc__db_wcroot_t * dst_wcroot,const char * dst_relpath,svn_wc__db_status_t dst_status,int dst_op_depth,int dst_np_op_depth,svn_node_kind_t kind,const apr_array_header_t * children,apr_int64_t copyfrom_id,const char * copyfrom_relpath,svn_revnum_t copyfrom_rev,apr_pool_t * scratch_pool)3885 cross_db_copy(svn_wc__db_wcroot_t *src_wcroot,
3886 const char *src_relpath,
3887 svn_wc__db_wcroot_t *dst_wcroot,
3888 const char *dst_relpath,
3889 svn_wc__db_status_t dst_status,
3890 int dst_op_depth,
3891 int dst_np_op_depth,
3892 svn_node_kind_t kind,
3893 const apr_array_header_t *children,
3894 apr_int64_t copyfrom_id,
3895 const char *copyfrom_relpath,
3896 svn_revnum_t copyfrom_rev,
3897 apr_pool_t *scratch_pool)
3898 {
3899 insert_working_baton_t iwb;
3900 svn_revnum_t changed_rev;
3901 apr_time_t changed_date;
3902 const char *changed_author;
3903 const svn_checksum_t *checksum;
3904 apr_hash_t *props;
3905 svn_depth_t depth;
3906
3907 SVN_ERR_ASSERT(kind == svn_node_file
3908 || kind == svn_node_dir
3909 );
3910
3911 SVN_ERR(read_info(NULL, NULL, NULL, NULL, NULL,
3912 &changed_rev, &changed_date, &changed_author, &depth,
3913 &checksum, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3914 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3915 src_wcroot, src_relpath, scratch_pool, scratch_pool));
3916
3917 if (dst_status != svn_wc__db_status_not_present
3918 && dst_status != svn_wc__db_status_excluded
3919 && dst_status != svn_wc__db_status_server_excluded)
3920 {
3921 SVN_ERR(db_read_pristine_props(&props, src_wcroot, src_relpath, FALSE,
3922 scratch_pool, scratch_pool));
3923 }
3924 else
3925 props = NULL;
3926
3927 blank_iwb(&iwb);
3928 iwb.presence = dst_status;
3929 iwb.kind = kind;
3930
3931 iwb.props = props;
3932 iwb.changed_rev = changed_rev;
3933 iwb.changed_date = changed_date;
3934 iwb.changed_author = changed_author;
3935 iwb.original_repos_id = copyfrom_id;
3936 iwb.original_repos_relpath = copyfrom_relpath;
3937 iwb.original_revnum = copyfrom_rev;
3938 iwb.moved_here = FALSE;
3939
3940 iwb.op_depth = dst_op_depth;
3941
3942 iwb.checksum = checksum;
3943 iwb.children = children;
3944 iwb.depth = depth;
3945
3946 iwb.not_present_op_depth = dst_np_op_depth;
3947
3948 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath, scratch_pool));
3949
3950 SVN_ERR(copy_actual(src_wcroot, src_relpath,
3951 dst_wcroot, dst_relpath, scratch_pool));
3952
3953 return SVN_NO_ERROR;
3954 }
3955
3956 /* Helper for scan_deletion_txn. Extracts the moved-to information, if
3957 any, from STMT. Sets *SCAN to FALSE if moved-to was available. */
3958 static svn_error_t *
get_moved_to(const char ** moved_to_relpath_p,const char ** moved_to_op_root_relpath_p,svn_boolean_t * scan,svn_sqlite__stmt_t * stmt,const char * current_relpath,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)3959 get_moved_to(const char **moved_to_relpath_p,
3960 const char **moved_to_op_root_relpath_p,
3961 svn_boolean_t *scan,
3962 svn_sqlite__stmt_t *stmt,
3963 const char *current_relpath,
3964 svn_wc__db_wcroot_t *wcroot,
3965 const char *local_relpath,
3966 apr_pool_t *result_pool,
3967 apr_pool_t *scratch_pool)
3968 {
3969 const char *moved_to_relpath = svn_sqlite__column_text(stmt, 3, NULL);
3970
3971 if (moved_to_relpath)
3972 {
3973 const char *moved_to_op_root_relpath = moved_to_relpath;
3974
3975 if (strcmp(current_relpath, local_relpath))
3976 {
3977 /* LOCAL_RELPATH is a child inside the move op-root. */
3978 const char *moved_child_relpath;
3979
3980 /* The CURRENT_RELPATH is the op_root of the delete-half of
3981 * the move. LOCAL_RELPATH is a child that was moved along.
3982 * Compute the child's new location within the move target. */
3983 moved_child_relpath = svn_relpath_skip_ancestor(current_relpath,
3984 local_relpath);
3985 SVN_ERR_ASSERT(moved_child_relpath &&
3986 strlen(moved_child_relpath) > 0);
3987 moved_to_relpath = svn_relpath_join(moved_to_op_root_relpath,
3988 moved_child_relpath,
3989 result_pool);
3990 }
3991
3992 if (moved_to_op_root_relpath && moved_to_op_root_relpath_p)
3993 *moved_to_op_root_relpath_p
3994 = apr_pstrdup(result_pool, moved_to_op_root_relpath);
3995
3996 if (moved_to_relpath && moved_to_relpath_p)
3997 *moved_to_relpath_p
3998 = apr_pstrdup(result_pool, moved_to_relpath);
3999
4000 *scan = FALSE;
4001 }
4002
4003 return SVN_NO_ERROR;
4004 }
4005
4006
4007 /* The body of svn_wc__db_scan_deletion().
4008 */
4009 static svn_error_t *
scan_deletion(const char ** base_del_relpath,const char ** moved_to_relpath,const char ** work_del_relpath,const char ** moved_to_op_root_relpath,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4010 scan_deletion(const char **base_del_relpath,
4011 const char **moved_to_relpath,
4012 const char **work_del_relpath,
4013 const char **moved_to_op_root_relpath,
4014 svn_wc__db_wcroot_t *wcroot,
4015 const char *local_relpath,
4016 apr_pool_t *result_pool,
4017 apr_pool_t *scratch_pool)
4018 {
4019 const char *current_relpath = local_relpath;
4020 svn_sqlite__stmt_t *stmt;
4021 svn_wc__db_status_t work_presence;
4022 svn_boolean_t have_row, scan, have_base;
4023 int op_depth;
4024
4025 /* Initialize all the OUT parameters. */
4026 if (base_del_relpath != NULL)
4027 *base_del_relpath = NULL;
4028 if (moved_to_relpath != NULL)
4029 *moved_to_relpath = NULL;
4030 if (work_del_relpath != NULL)
4031 *work_del_relpath = NULL;
4032 if (moved_to_op_root_relpath != NULL)
4033 *moved_to_op_root_relpath = NULL;
4034
4035 /* If looking for moved-to info then we need to scan every path
4036 until we find it. If not looking for moved-to we only need to
4037 check op-roots and parents of op-roots. */
4038 scan = (moved_to_op_root_relpath || moved_to_relpath);
4039
4040 SVN_ERR(svn_sqlite__get_statement(
4041 &stmt, wcroot->sdb, STMT_SELECT_DELETION_INFO));
4042
4043 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
4044 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4045 if (!have_row)
4046 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
4047 _("The node '%s' was not found."),
4048 path_for_error_message(wcroot, local_relpath,
4049 scratch_pool));
4050
4051 work_presence = svn_sqlite__column_token(stmt, 1, presence_map);
4052 have_base = !svn_sqlite__column_is_null(stmt, 0);
4053 if (work_presence != svn_wc__db_status_not_present
4054 && work_presence != svn_wc__db_status_base_deleted)
4055 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
4056 svn_sqlite__reset(stmt),
4057 _("Expected node '%s' to be deleted."),
4058 path_for_error_message(wcroot, local_relpath,
4059 scratch_pool));
4060
4061 op_depth = svn_sqlite__column_int(stmt, 2);
4062
4063 /* Special case: LOCAL_RELPATH not-present within a WORKING tree, we
4064 treat this as an op-root. At commit time we need to explicitly
4065 delete such nodes otherwise they will be present in the
4066 repository copy. */
4067 if (work_presence == svn_wc__db_status_not_present
4068 && work_del_relpath && !*work_del_relpath)
4069 {
4070 *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
4071
4072 if (!scan && !base_del_relpath)
4073 {
4074 /* We have all we need, exit early */
4075 SVN_ERR(svn_sqlite__reset(stmt));
4076 return SVN_NO_ERROR;
4077 }
4078 }
4079
4080
4081 while (TRUE)
4082 {
4083 svn_error_t *err;
4084 const char *parent_relpath;
4085 int current_depth = relpath_depth(current_relpath);
4086
4087 /* Step CURRENT_RELPATH to op-root */
4088
4089 while (TRUE)
4090 {
4091 if (scan)
4092 {
4093 err = get_moved_to(moved_to_relpath, moved_to_op_root_relpath,
4094 &scan, stmt, current_relpath,
4095 wcroot, local_relpath,
4096 result_pool, scratch_pool);
4097 if (err || (!scan
4098 && !base_del_relpath
4099 && !work_del_relpath))
4100 {
4101 /* We have all we need (or an error occurred) */
4102 SVN_ERR(svn_sqlite__reset(stmt));
4103 return svn_error_trace(err);
4104 }
4105 }
4106
4107 if (current_depth <= op_depth)
4108 break;
4109
4110 current_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4111 --current_depth;
4112
4113 if (scan || current_depth == op_depth)
4114 {
4115 SVN_ERR(svn_sqlite__reset(stmt));
4116 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
4117 current_relpath));
4118 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4119 SVN_ERR_ASSERT(have_row);
4120 have_base = !svn_sqlite__column_is_null(stmt, 0);
4121 }
4122 }
4123 SVN_ERR(svn_sqlite__reset(stmt));
4124
4125 /* Now CURRENT_RELPATH is an op-root, have a look at the parent. */
4126
4127 SVN_ERR_ASSERT(current_relpath[0] != '\0'); /* Catch invalid data */
4128 parent_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4129 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4130 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4131 if (!have_row)
4132 {
4133 /* No row means no WORKING node which mean we just fell off
4134 the WORKING tree, so CURRENT_RELPATH is the op-root
4135 closest to the wc root. */
4136 if (have_base && base_del_relpath)
4137 *base_del_relpath = apr_pstrdup(result_pool, current_relpath);
4138 break;
4139 }
4140
4141 /* Still in the WORKING tree so the first time we get here
4142 CURRENT_RELPATH is a delete op-root in the WORKING tree. */
4143 if (work_del_relpath && !*work_del_relpath)
4144 {
4145 *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
4146
4147 if (!scan && !base_del_relpath)
4148 break; /* We have all we need */
4149 }
4150
4151 current_relpath = parent_relpath;
4152 op_depth = svn_sqlite__column_int(stmt, 2);
4153 have_base = !svn_sqlite__column_is_null(stmt, 0);
4154 }
4155
4156 SVN_ERR(svn_sqlite__reset(stmt));
4157
4158 return SVN_NO_ERROR;
4159 }
4160
4161 svn_error_t *
svn_wc__db_scan_deletion_internal(const char ** base_del_relpath,const char ** moved_to_relpath,const char ** work_del_relpath,const char ** moved_to_op_root_relpath,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4162 svn_wc__db_scan_deletion_internal(
4163 const char **base_del_relpath,
4164 const char **moved_to_relpath,
4165 const char **work_del_relpath,
4166 const char **moved_to_op_root_relpath,
4167 svn_wc__db_wcroot_t *wcroot,
4168 const char *local_relpath,
4169 apr_pool_t *result_pool,
4170 apr_pool_t *scratch_pool)
4171 {
4172 return svn_error_trace(
4173 scan_deletion(base_del_relpath, moved_to_relpath, work_del_relpath,
4174 moved_to_op_root_relpath,
4175 wcroot, local_relpath,
4176 result_pool, scratch_pool));
4177 }
4178
4179
4180 svn_error_t *
svn_wc__db_scan_deletion(const char ** base_del_abspath,const char ** moved_to_abspath,const char ** work_del_abspath,const char ** moved_to_op_root_abspath,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4181 svn_wc__db_scan_deletion(const char **base_del_abspath,
4182 const char **moved_to_abspath,
4183 const char **work_del_abspath,
4184 const char **moved_to_op_root_abspath,
4185 svn_wc__db_t *db,
4186 const char *local_abspath,
4187 apr_pool_t *result_pool,
4188 apr_pool_t *scratch_pool)
4189 {
4190 svn_wc__db_wcroot_t *wcroot;
4191 const char *local_relpath;
4192 const char *base_del_relpath, *moved_to_relpath, *work_del_relpath;
4193 const char *moved_to_op_root_relpath;
4194
4195 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4196
4197 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4198 local_abspath, scratch_pool, scratch_pool));
4199 VERIFY_USABLE_WCROOT(wcroot);
4200
4201 SVN_WC__DB_WITH_TXN(
4202 scan_deletion(&base_del_relpath, &moved_to_relpath,
4203 &work_del_relpath, &moved_to_op_root_relpath,
4204 wcroot, local_relpath, result_pool, scratch_pool),
4205 wcroot);
4206
4207 if (base_del_abspath)
4208 {
4209 *base_del_abspath = (base_del_relpath
4210 ? svn_dirent_join(wcroot->abspath,
4211 base_del_relpath, result_pool)
4212 : NULL);
4213 }
4214 if (moved_to_abspath)
4215 {
4216 *moved_to_abspath = (moved_to_relpath
4217 ? svn_dirent_join(wcroot->abspath,
4218 moved_to_relpath, result_pool)
4219 : NULL);
4220 }
4221 if (work_del_abspath)
4222 {
4223 *work_del_abspath = (work_del_relpath
4224 ? svn_dirent_join(wcroot->abspath,
4225 work_del_relpath, result_pool)
4226 : NULL);
4227 }
4228 if (moved_to_op_root_abspath)
4229 {
4230 *moved_to_op_root_abspath = (moved_to_op_root_relpath
4231 ? svn_dirent_join(wcroot->abspath,
4232 moved_to_op_root_relpath,
4233 result_pool)
4234 : NULL);
4235 }
4236
4237 return SVN_NO_ERROR;
4238 }
4239
4240
4241 /* Set *COPYFROM_ID, *COPYFROM_RELPATH, *COPYFROM_REV to the values
4242 appropriate for the copy. Also return *STATUS, *KIND and *HAVE_WORK, *OP_ROOT
4243 since they are available. This is a helper for
4244 svn_wc__db_op_copy. */
4245 static svn_error_t *
get_info_for_copy(apr_int64_t * copyfrom_id,const char ** copyfrom_relpath,svn_revnum_t * copyfrom_rev,svn_wc__db_status_t * status,svn_node_kind_t * kind,svn_boolean_t * op_root,svn_wc__db_wcroot_t * src_wcroot,const char * local_relpath,svn_wc__db_wcroot_t * dst_wcroot,apr_pool_t * result_pool,apr_pool_t * scratch_pool)4246 get_info_for_copy(apr_int64_t *copyfrom_id,
4247 const char **copyfrom_relpath,
4248 svn_revnum_t *copyfrom_rev,
4249 svn_wc__db_status_t *status,
4250 svn_node_kind_t *kind,
4251 svn_boolean_t *op_root,
4252 svn_wc__db_wcroot_t *src_wcroot,
4253 const char *local_relpath,
4254 svn_wc__db_wcroot_t *dst_wcroot,
4255 apr_pool_t *result_pool,
4256 apr_pool_t *scratch_pool)
4257 {
4258 const char *repos_relpath;
4259 svn_revnum_t revision;
4260 svn_wc__db_status_t node_status;
4261 apr_int64_t repos_id;
4262 svn_boolean_t is_op_root;
4263
4264 SVN_ERR(read_info(&node_status, kind, &revision, &repos_relpath, &repos_id,
4265 NULL, NULL, NULL, NULL, NULL, NULL, copyfrom_relpath,
4266 copyfrom_id, copyfrom_rev, NULL, NULL, NULL, NULL,
4267 NULL, &is_op_root, NULL, NULL,
4268 NULL /* have_base */,
4269 NULL /* have_more_work */,
4270 NULL /* have_work */,
4271 src_wcroot, local_relpath, result_pool, scratch_pool));
4272
4273 if (op_root)
4274 *op_root = is_op_root;
4275
4276 if (node_status == svn_wc__db_status_excluded)
4277 {
4278 /* The parent cannot be excluded, so look at the parent and then
4279 adjust the relpath */
4280 const char *parent_relpath, *base_name;
4281
4282 svn_dirent_split(&parent_relpath, &base_name, local_relpath,
4283 scratch_pool);
4284 SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev,
4285 NULL, NULL, NULL,
4286 src_wcroot, parent_relpath, dst_wcroot,
4287 scratch_pool, scratch_pool));
4288 if (*copyfrom_relpath)
4289 *copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, base_name,
4290 result_pool);
4291 }
4292 else if (node_status == svn_wc__db_status_added)
4293 {
4294 SVN_ERR(scan_addition(&node_status, NULL, NULL, NULL, NULL, NULL, NULL,
4295 NULL, NULL, NULL, src_wcroot, local_relpath,
4296 scratch_pool, scratch_pool));
4297 }
4298 else if (node_status == svn_wc__db_status_deleted && is_op_root)
4299 {
4300 const char *base_del_relpath, *work_del_relpath;
4301
4302 SVN_ERR(scan_deletion(&base_del_relpath, NULL,
4303 &work_del_relpath,
4304 NULL, src_wcroot, local_relpath,
4305 scratch_pool, scratch_pool));
4306 if (work_del_relpath)
4307 {
4308 const char *op_root_relpath;
4309 const char *parent_del_relpath = svn_relpath_dirname(work_del_relpath,
4310 scratch_pool);
4311
4312 /* Similar to, but not the same as, the _scan_addition and
4313 _join above. Can we use get_copyfrom here? */
4314 SVN_ERR(scan_addition(NULL, &op_root_relpath,
4315 NULL, NULL, /* repos_* */
4316 copyfrom_relpath, copyfrom_id, copyfrom_rev,
4317 NULL, NULL, NULL,
4318 src_wcroot, parent_del_relpath,
4319 scratch_pool, scratch_pool));
4320 *copyfrom_relpath
4321 = svn_relpath_join(*copyfrom_relpath,
4322 svn_relpath_skip_ancestor(op_root_relpath,
4323 local_relpath),
4324 result_pool);
4325 }
4326 else if (base_del_relpath)
4327 {
4328 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, copyfrom_rev,
4329 copyfrom_relpath,
4330 copyfrom_id, NULL, NULL,
4331 NULL, NULL, NULL, NULL,
4332 NULL, NULL, NULL, NULL,
4333 src_wcroot, local_relpath,
4334 result_pool,
4335 scratch_pool));
4336 }
4337 else
4338 SVN_ERR_MALFUNCTION();
4339 }
4340 else if (node_status == svn_wc__db_status_deleted)
4341 {
4342 /* Keep original_* from read_info() to allow seeing the difference
4343 between base-deleted and not present */
4344 }
4345 else
4346 {
4347 *copyfrom_relpath = repos_relpath;
4348 *copyfrom_rev = revision;
4349 *copyfrom_id = repos_id;
4350 }
4351
4352 if (status)
4353 *status = node_status;
4354
4355 if (src_wcroot != dst_wcroot && *copyfrom_relpath)
4356 {
4357 const char *repos_root_url;
4358 const char *repos_uuid;
4359
4360 /* Pass the right repos-id for the destination db. We can't just use
4361 the id of the source database, as this value can change after
4362 relocation (and perhaps also when we start storing multiple
4363 working copies in a single db)! */
4364
4365 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
4366 src_wcroot, *copyfrom_id,
4367 scratch_pool));
4368
4369 SVN_ERR(create_repos_id(copyfrom_id, repos_root_url, repos_uuid,
4370 dst_wcroot->sdb, scratch_pool));
4371 }
4372
4373 return SVN_NO_ERROR;
4374 }
4375
4376
4377 /* Set *OP_DEPTH to the highest op depth of WCROOT:LOCAL_RELPATH. */
4378 static svn_error_t *
op_depth_of(int * op_depth,svn_wc__db_wcroot_t * wcroot,const char * local_relpath)4379 op_depth_of(int *op_depth,
4380 svn_wc__db_wcroot_t *wcroot,
4381 const char *local_relpath)
4382 {
4383 svn_sqlite__stmt_t *stmt;
4384 svn_boolean_t have_row;
4385
4386 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4387 STMT_SELECT_NODE_INFO));
4388 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4389 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4390 SVN_ERR_ASSERT(have_row);
4391 *op_depth = svn_sqlite__column_int(stmt, 0);
4392 SVN_ERR(svn_sqlite__reset(stmt));
4393
4394 return SVN_NO_ERROR;
4395 }
4396
4397
4398 /* Determine at which OP_DEPTH a copy of COPYFROM_REPOS_ID, COPYFROM_RELPATH at
4399 revision COPYFROM_REVISION should be inserted as LOCAL_RELPATH. Do this
4400 by checking if this would be a direct child of a copy of its parent
4401 directory. If it is then set *OP_DEPTH to the op_depth of its parent.
4402
4403 If the node is not a direct copy at the same revision of the parent
4404 *NP_OP_DEPTH will be set to the op_depth of the parent when a not-present
4405 node should be inserted at this op_depth. This will be the case when the
4406 parent already defined an incomplete child with the same name. Otherwise
4407 *NP_OP_DEPTH will be set to -1.
4408
4409 If the parent node is not the parent of the to be copied node, then
4410 *OP_DEPTH will be set to the proper op_depth for a new operation root.
4411
4412 Set *PARENT_OP_DEPTH to the op_depth of the parent.
4413
4414 */
4415 static svn_error_t *
op_depth_for_copy(int * op_depth,int * np_op_depth,int * parent_op_depth,apr_int64_t copyfrom_repos_id,const char * copyfrom_relpath,svn_revnum_t copyfrom_revision,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)4416 op_depth_for_copy(int *op_depth,
4417 int *np_op_depth,
4418 int *parent_op_depth,
4419 apr_int64_t copyfrom_repos_id,
4420 const char *copyfrom_relpath,
4421 svn_revnum_t copyfrom_revision,
4422 svn_wc__db_wcroot_t *wcroot,
4423 const char *local_relpath,
4424 apr_pool_t *scratch_pool)
4425 {
4426 const char *parent_relpath, *name;
4427 svn_sqlite__stmt_t *stmt;
4428 svn_boolean_t have_row;
4429 int incomplete_op_depth = -1;
4430 int min_op_depth = 1; /* Never touch BASE */
4431
4432 *op_depth = relpath_depth(local_relpath);
4433 *np_op_depth = -1;
4434
4435 svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool);
4436 *parent_op_depth = relpath_depth(parent_relpath);
4437
4438 if (!copyfrom_relpath)
4439 return SVN_NO_ERROR;
4440
4441 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4442 STMT_SELECT_WORKING_NODE));
4443 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4444 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4445 if (have_row)
4446 {
4447 svn_wc__db_status_t status = svn_sqlite__column_token(stmt, 1,
4448 presence_map);
4449
4450 min_op_depth = svn_sqlite__column_int(stmt, 0);
4451 if (status == svn_wc__db_status_incomplete)
4452 incomplete_op_depth = min_op_depth;
4453 }
4454 SVN_ERR(svn_sqlite__reset(stmt));
4455
4456 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4457 STMT_SELECT_WORKING_NODE));
4458 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4459 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4460 if (have_row)
4461 {
4462 svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 1,
4463 presence_map);
4464
4465 *parent_op_depth = svn_sqlite__column_int(stmt, 0);
4466 if (*parent_op_depth < min_op_depth)
4467 {
4468 /* We want to create a copy; not overwrite the lower layers */
4469 SVN_ERR(svn_sqlite__reset(stmt));
4470 return SVN_NO_ERROR;
4471 }
4472
4473 /* You can only add children below a node that exists.
4474 In WORKING that must be status added, which is represented
4475 as presence normal */
4476 SVN_ERR_ASSERT(presence == svn_wc__db_status_normal);
4477
4478 if ((incomplete_op_depth < 0)
4479 || (incomplete_op_depth == *parent_op_depth))
4480 {
4481 apr_int64_t parent_copyfrom_repos_id
4482 = svn_sqlite__column_int64(stmt, 10);
4483 const char *parent_copyfrom_relpath
4484 = svn_sqlite__column_text(stmt, 11, NULL);
4485 svn_revnum_t parent_copyfrom_revision
4486 = svn_sqlite__column_revnum(stmt, 12);
4487
4488 if (parent_copyfrom_repos_id == copyfrom_repos_id)
4489 {
4490 if (copyfrom_revision == parent_copyfrom_revision
4491 && !strcmp(copyfrom_relpath,
4492 svn_relpath_join(parent_copyfrom_relpath, name,
4493 scratch_pool)))
4494 *op_depth = *parent_op_depth;
4495 else if (incomplete_op_depth > 0)
4496 *np_op_depth = incomplete_op_depth;
4497 }
4498 }
4499 }
4500 SVN_ERR(svn_sqlite__reset(stmt));
4501
4502 return SVN_NO_ERROR;
4503 }
4504
4505
4506 /* Like svn_wc__db_op_copy(), but with WCROOT+LOCAL_RELPATH
4507 * instead of DB+LOCAL_ABSPATH. A non-zero MOVE_OP_DEPTH implies that the
4508 * copy operation is part of a move, and indicates the op-depth of the
4509 * move destination op-root. */
4510 static svn_error_t *
db_op_copy(svn_wc__db_wcroot_t * src_wcroot,const char * src_relpath,svn_wc__db_wcroot_t * dst_wcroot,const char * dst_relpath,const svn_skel_t * work_items,int move_op_depth,apr_pool_t * scratch_pool)4511 db_op_copy(svn_wc__db_wcroot_t *src_wcroot,
4512 const char *src_relpath,
4513 svn_wc__db_wcroot_t *dst_wcroot,
4514 const char *dst_relpath,
4515 const svn_skel_t *work_items,
4516 int move_op_depth,
4517 apr_pool_t *scratch_pool)
4518 {
4519 const char *copyfrom_relpath;
4520 svn_revnum_t copyfrom_rev;
4521 svn_wc__db_status_t status;
4522 svn_wc__db_status_t dst_presence;
4523 svn_boolean_t op_root;
4524 apr_int64_t copyfrom_id;
4525 int dst_op_depth;
4526 int dst_np_op_depth;
4527 int dst_parent_op_depth;
4528 svn_node_kind_t kind;
4529 const apr_array_header_t *children;
4530
4531 SVN_ERR(get_info_for_copy(©from_id, ©from_relpath, ©from_rev,
4532 &status, &kind, &op_root,
4533 src_wcroot, src_relpath, dst_wcroot,
4534 scratch_pool, scratch_pool));
4535
4536 SVN_ERR(op_depth_for_copy(&dst_op_depth, &dst_np_op_depth,
4537 &dst_parent_op_depth,
4538 copyfrom_id, copyfrom_relpath, copyfrom_rev,
4539 dst_wcroot, dst_relpath, scratch_pool));
4540
4541 SVN_ERR_ASSERT(kind == svn_node_file || kind == svn_node_dir);
4542
4543 /* ### New status, not finished, see notes/wc-ng/copying */
4544 switch (status)
4545 {
4546 case svn_wc__db_status_normal:
4547 case svn_wc__db_status_added:
4548 case svn_wc__db_status_moved_here:
4549 case svn_wc__db_status_copied:
4550 dst_presence = svn_wc__db_status_normal;
4551 break;
4552 case svn_wc__db_status_deleted:
4553 if (op_root)
4554 {
4555 /* If the lower layer is already shadowcopied we can skip adding
4556 a not present node. */
4557 svn_error_t *err;
4558 svn_wc__db_status_t dst_status;
4559
4560 err = read_info(&dst_status, NULL, NULL, NULL, NULL, NULL, NULL,
4561 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4562 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4563 dst_wcroot, dst_relpath, scratch_pool, scratch_pool);
4564
4565 if (err)
4566 {
4567 if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
4568 svn_error_clear(err);
4569 else
4570 return svn_error_trace(err);
4571 }
4572 else if (dst_status == svn_wc__db_status_deleted)
4573 {
4574 /* Node is already deleted; skip the NODES work, but do
4575 install wq items if requested */
4576 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4577 scratch_pool));
4578 return SVN_NO_ERROR;
4579 }
4580 }
4581 else
4582 {
4583 /* This node is either a not-present node (which should be copied), or
4584 a base-delete of some lower layer (which shouldn't).
4585 Subversion <= 1.7 always added a not-present node here, which is
4586 safe (as it postpones the hard work until commit time and then we
4587 ask the repository), but it breaks some move scenarios.
4588 */
4589
4590 if (! copyfrom_relpath)
4591 {
4592 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4593 scratch_pool));
4594 return SVN_NO_ERROR;
4595 }
4596
4597 /* Fall through. Install not present node */
4598 }
4599 case svn_wc__db_status_not_present:
4600 case svn_wc__db_status_excluded:
4601 /* These presence values should not create a new op depth */
4602 if (dst_np_op_depth > 0)
4603 {
4604 dst_op_depth = dst_np_op_depth;
4605 dst_np_op_depth = -1;
4606 }
4607 if (status == svn_wc__db_status_excluded)
4608 dst_presence = svn_wc__db_status_excluded;
4609 else
4610 dst_presence = svn_wc__db_status_not_present;
4611 break;
4612 case svn_wc__db_status_server_excluded:
4613 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4614 _("Cannot copy '%s' excluded by server"),
4615 path_for_error_message(src_wcroot,
4616 src_relpath,
4617 scratch_pool));
4618 default:
4619 /* Perhaps we should allow incomplete to incomplete? We can't
4620 avoid incomplete working nodes as one step in copying a
4621 directory is to add incomplete children. */
4622 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4623 _("Cannot handle status of '%s'"),
4624 path_for_error_message(src_wcroot,
4625 src_relpath,
4626 scratch_pool));
4627 }
4628
4629 if (kind == svn_node_dir)
4630 {
4631 int src_op_depth;
4632
4633 SVN_ERR(op_depth_of(&src_op_depth, src_wcroot, src_relpath));
4634 SVN_ERR(gather_children(&children, src_wcroot, src_relpath,
4635 STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth,
4636 scratch_pool, scratch_pool));
4637 }
4638 else
4639 children = NULL;
4640
4641 if (src_wcroot == dst_wcroot)
4642 {
4643 svn_sqlite__stmt_t *stmt;
4644 const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
4645 scratch_pool);
4646
4647 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
4648 STMT_INSERT_WORKING_NODE_COPY_FROM));
4649
4650 SVN_ERR(svn_sqlite__bindf(stmt, "issdst",
4651 src_wcroot->wc_id, src_relpath,
4652 dst_relpath,
4653 dst_op_depth,
4654 dst_parent_relpath,
4655 presence_map, dst_presence));
4656
4657 if (move_op_depth > 0)
4658 {
4659 if (relpath_depth(dst_relpath) == move_op_depth)
4660 {
4661 /* We're moving the root of the move operation.
4662 *
4663 * When an added node or the op-root of a copy is moved,
4664 * there is no 'moved-from' corresponding to the moved-here
4665 * node. So the net effect is the same as copy+delete.
4666 * Perform a normal copy operation in these cases. */
4667 if (!(status == svn_wc__db_status_added ||
4668 (status == svn_wc__db_status_copied && op_root)))
4669 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4670 }
4671 else
4672 {
4673 svn_sqlite__stmt_t *info_stmt;
4674 svn_boolean_t have_row;
4675
4676 /* We're moving a child along with the root of the move.
4677 *
4678 * Set moved-here depending on dst_parent, propagating the
4679 * above decision to moved-along children at the same op_depth.
4680 * We can't use scan_addition() to detect moved-here because
4681 * the delete-half of the move might not yet exist. */
4682 SVN_ERR(svn_sqlite__get_statement(&info_stmt, dst_wcroot->sdb,
4683 STMT_SELECT_NODE_INFO));
4684 SVN_ERR(svn_sqlite__bindf(info_stmt, "is", dst_wcroot->wc_id,
4685 dst_parent_relpath));
4686 SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4687 SVN_ERR_ASSERT(have_row);
4688 if (svn_sqlite__column_boolean(info_stmt, 15) &&
4689 dst_op_depth == dst_parent_op_depth)
4690 {
4691 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4692 SVN_ERR(svn_sqlite__reset(info_stmt));
4693 }
4694 else
4695 {
4696 SVN_ERR(svn_sqlite__reset(info_stmt));
4697
4698 /* If the child has been moved into the tree we're moving,
4699 * keep its moved-here bit set. */
4700 SVN_ERR(svn_sqlite__get_statement(&info_stmt,
4701 dst_wcroot->sdb,
4702 STMT_SELECT_NODE_INFO));
4703 SVN_ERR(svn_sqlite__bindf(info_stmt, "is",
4704 dst_wcroot->wc_id, src_relpath));
4705 SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4706 SVN_ERR_ASSERT(have_row);
4707 if (svn_sqlite__column_boolean(info_stmt, 15))
4708 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4709 SVN_ERR(svn_sqlite__reset(info_stmt));
4710 }
4711 }
4712 }
4713
4714 SVN_ERR(svn_sqlite__step_done(stmt));
4715
4716 /* ### Copying changelist is OK for a move but what about a copy? */
4717 SVN_ERR(copy_actual(src_wcroot, src_relpath,
4718 dst_wcroot, dst_relpath, scratch_pool));
4719
4720 if (dst_np_op_depth > 0)
4721 {
4722 /* We introduce a not-present node at the parent's op_depth to
4723 properly start a new op-depth at our own op_depth. This marks
4724 us as an op_root for commit and allows reverting just this
4725 operation */
4726
4727 SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
4728 STMT_INSERT_NODE));
4729 SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
4730 src_wcroot->wc_id, dst_relpath,
4731 dst_np_op_depth, dst_parent_relpath,
4732 copyfrom_id, copyfrom_relpath,
4733 copyfrom_rev,
4734 presence_map,
4735 svn_wc__db_status_not_present,
4736 /* NULL */
4737 kind_map, kind));
4738
4739 SVN_ERR(svn_sqlite__step_done(stmt));
4740 }
4741 /* Insert incomplete children, if relevant.
4742 The children are part of the same op and so have the same op_depth.
4743 (The only time we'd want a different depth is during a recursive
4744 simple add, but we never insert children here during a simple add.) */
4745 if (kind == svn_node_dir
4746 && dst_presence == svn_wc__db_status_normal)
4747 SVN_ERR(insert_incomplete_children(
4748 dst_wcroot->sdb,
4749 dst_wcroot->wc_id,
4750 dst_relpath,
4751 copyfrom_id,
4752 copyfrom_relpath,
4753 copyfrom_rev,
4754 children,
4755 dst_op_depth,
4756 scratch_pool));
4757 }
4758 else
4759 {
4760 SVN_ERR(cross_db_copy(src_wcroot, src_relpath, dst_wcroot,
4761 dst_relpath, dst_presence, dst_op_depth,
4762 dst_np_op_depth, kind,
4763 children, copyfrom_id, copyfrom_relpath,
4764 copyfrom_rev, scratch_pool));
4765 }
4766
4767 SVN_ERR(add_work_items(dst_wcroot->sdb, work_items, scratch_pool));
4768
4769 return SVN_NO_ERROR;
4770 }
4771
4772 /* Baton for passing args to op_copy_txn(). */
4773 struct op_copy_baton
4774 {
4775 svn_wc__db_wcroot_t *src_wcroot;
4776 const char *src_relpath;
4777
4778 svn_wc__db_wcroot_t *dst_wcroot;
4779 const char *dst_relpath;
4780
4781 const svn_skel_t *work_items;
4782
4783 svn_boolean_t is_move;
4784 const char *dst_op_root_relpath;
4785 };
4786
4787 /* Helper for svn_wc__db_op_copy(). */
4788 static svn_error_t *
op_copy_txn(svn_wc__db_wcroot_t * wcroot,struct op_copy_baton * ocb,apr_pool_t * scratch_pool)4789 op_copy_txn(svn_wc__db_wcroot_t *wcroot,
4790 struct op_copy_baton *ocb,
4791 apr_pool_t *scratch_pool)
4792 {
4793 int move_op_depth;
4794
4795 if (wcroot != ocb->dst_wcroot)
4796 {
4797 /* Source and destination databases differ; so also start a lock
4798 in the destination database, by calling ourself in an extra lock. */
4799
4800 SVN_WC__DB_WITH_TXN(op_copy_txn(ocb->dst_wcroot, ocb, scratch_pool),
4801 ocb->dst_wcroot);
4802
4803 return SVN_NO_ERROR;
4804 }
4805
4806 /* From this point we can assume a lock in the src and dst databases */
4807
4808 if (ocb->is_move)
4809 move_op_depth = relpath_depth(ocb->dst_op_root_relpath);
4810 else
4811 move_op_depth = 0;
4812
4813 SVN_ERR(db_op_copy(ocb->src_wcroot, ocb->src_relpath,
4814 ocb->dst_wcroot, ocb->dst_relpath,
4815 ocb->work_items, move_op_depth, scratch_pool));
4816
4817 return SVN_NO_ERROR;
4818 }
4819
4820 svn_error_t *
svn_wc__db_op_copy(svn_wc__db_t * db,const char * src_abspath,const char * dst_abspath,const char * dst_op_root_abspath,svn_boolean_t is_move,const svn_skel_t * work_items,apr_pool_t * scratch_pool)4821 svn_wc__db_op_copy(svn_wc__db_t *db,
4822 const char *src_abspath,
4823 const char *dst_abspath,
4824 const char *dst_op_root_abspath,
4825 svn_boolean_t is_move,
4826 const svn_skel_t *work_items,
4827 apr_pool_t *scratch_pool)
4828 {
4829 struct op_copy_baton ocb = {0};
4830
4831 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
4832 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
4833 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_op_root_abspath));
4834
4835 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
4836 &ocb.src_relpath, db,
4837 src_abspath,
4838 scratch_pool, scratch_pool));
4839 VERIFY_USABLE_WCROOT(ocb.src_wcroot);
4840
4841 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
4842 &ocb.dst_relpath,
4843 db, dst_abspath,
4844 scratch_pool, scratch_pool));
4845 VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
4846
4847 ocb.work_items = work_items;
4848 ocb.is_move = is_move;
4849 ocb.dst_op_root_relpath = svn_dirent_skip_ancestor(ocb.dst_wcroot->abspath,
4850 dst_op_root_abspath);
4851
4852 /* Call with the sdb in src_wcroot. It might call itself again to
4853 also obtain a lock in dst_wcroot */
4854 SVN_WC__DB_WITH_TXN(op_copy_txn(ocb.src_wcroot, &ocb, scratch_pool),
4855 ocb.src_wcroot);
4856
4857 return SVN_NO_ERROR;
4858 }
4859
4860 /* Remove unneeded actual nodes for svn_wc__db_op_copy_layer_internal */
4861 static svn_error_t *
clear_or_remove_actual(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int op_depth,apr_pool_t * scratch_pool)4862 clear_or_remove_actual(svn_wc__db_wcroot_t *wcroot,
4863 const char *local_relpath,
4864 int op_depth,
4865 apr_pool_t *scratch_pool)
4866 {
4867 svn_sqlite__stmt_t *stmt;
4868 svn_boolean_t have_row, shadowed;
4869 svn_boolean_t keep_conflict = FALSE;
4870
4871 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4872 STMT_SELECT_NODE_INFO));
4873
4874 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4875 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4876
4877 if (have_row)
4878 {
4879 svn_wc__db_status_t presence;
4880
4881 shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth);
4882 presence = svn_sqlite__column_token(stmt, 3, presence_map);
4883
4884 if (shadowed && presence == svn_wc__db_status_base_deleted)
4885 {
4886 keep_conflict = TRUE;
4887 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4888
4889 if (have_row)
4890 shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth);
4891 else
4892 shadowed = FALSE;
4893 }
4894 }
4895 else
4896 shadowed = FALSE;
4897
4898 SVN_ERR(svn_sqlite__reset(stmt));
4899 if (shadowed)
4900 return SVN_NO_ERROR;
4901
4902 if (keep_conflict)
4903 {
4904 /* We don't want to accidentally remove delete-delete conflicts */
4905 SVN_ERR(svn_sqlite__get_statement(
4906 &stmt, wcroot->sdb,
4907 STMT_CLEAR_ACTUAL_NODE_LEAVING_CONFLICT));
4908 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4909 SVN_ERR(svn_sqlite__step_done(stmt));
4910 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4911 STMT_DELETE_ACTUAL_EMPTY));
4912 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4913 SVN_ERR(svn_sqlite__step_done(stmt));
4914 }
4915 else
4916 {
4917 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4918 STMT_DELETE_ACTUAL_NODE));
4919 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4920 SVN_ERR(svn_sqlite__step_done(stmt));
4921 }
4922
4923 return SVN_NO_ERROR;
4924 }
4925
4926 svn_error_t *
svn_wc__db_op_copy_layer_internal(svn_wc__db_wcroot_t * wcroot,const char * src_op_relpath,int src_op_depth,const char * dst_op_relpath,svn_skel_t * conflict,svn_skel_t * work_items,apr_pool_t * scratch_pool)4927 svn_wc__db_op_copy_layer_internal(svn_wc__db_wcroot_t *wcroot,
4928 const char *src_op_relpath,
4929 int src_op_depth,
4930 const char *dst_op_relpath,
4931 svn_skel_t *conflict,
4932 svn_skel_t *work_items,
4933 apr_pool_t *scratch_pool)
4934 {
4935 svn_sqlite__stmt_t *stmt, *stmt2;
4936 svn_boolean_t have_row;
4937 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4938 int dst_op_depth = relpath_depth(dst_op_relpath);
4939 svn_boolean_t locked;
4940 svn_error_t *err = NULL;
4941
4942 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, dst_op_relpath,
4943 FALSE, scratch_pool));
4944
4945 if (!locked)
4946 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
4947 _("No write-lock in '%s'"),
4948 path_for_error_message(wcroot, dst_op_relpath,
4949 scratch_pool));
4950
4951 SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
4952 STMT_COPY_NODE_MOVE));
4953
4954 /* Replace entire subtree at one op-depth. */
4955 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4956 STMT_SELECT_LAYER_FOR_REPLACE));
4957 SVN_ERR(svn_sqlite__bindf(stmt, "isdsd", wcroot->wc_id,
4958 src_op_relpath, src_op_depth,
4959 dst_op_relpath, dst_op_depth));
4960 SVN_ERR(svn_sqlite__step(&have_row, stmt));
4961 while (have_row)
4962 {
4963 const char *src_relpath;
4964 const char *dst_relpath;
4965
4966 svn_pool_clear(iterpool);
4967
4968 src_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
4969 dst_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
4970
4971 err = svn_sqlite__bindf(stmt2, "isdsds", wcroot->wc_id,
4972 src_relpath, src_op_depth,
4973 dst_relpath, dst_op_depth,
4974 svn_relpath_dirname(dst_relpath, iterpool));
4975 if (!err)
4976 err = svn_sqlite__step_done(stmt2);
4977
4978 /* stmt2 is reset (never modified or by step_done) */
4979
4980 if (err)
4981 break;
4982
4983 /* The node can't be deleted where it is added, so extension of
4984 an existing shadowing is only interesting 2 levels deep. */
4985 if (relpath_depth(dst_relpath) > (dst_op_depth+1))
4986 {
4987 svn_boolean_t exists = !svn_sqlite__column_is_null(stmt, 3);
4988
4989 if (exists)
4990 {
4991 svn_wc__db_status_t presence;
4992
4993 presence = svn_sqlite__column_token(stmt, 3, presence_map);
4994
4995 if (presence != svn_wc__db_status_normal)
4996 exists = FALSE;
4997 }
4998
4999 if (!exists)
5000 {
5001 svn_node_kind_t kind = svn_sqlite__column_token(stmt, 1, kind_map);
5002
5003 err = db_extend_parent_delete(wcroot, dst_relpath,
5004 kind, dst_op_depth, iterpool);
5005
5006 if (err)
5007 break;
5008 }
5009 }
5010
5011 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5012 }
5013
5014 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
5015
5016 /* And now remove the records that are no longer needed */
5017 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5018 STMT_SELECT_NO_LONGER_MOVED_RV));
5019 SVN_ERR(svn_sqlite__bindf(stmt, "isdsd", wcroot->wc_id,
5020 dst_op_relpath, dst_op_depth,
5021 src_op_relpath, src_op_depth));
5022 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5023 while (have_row)
5024 {
5025 const char *dst_relpath;
5026 svn_wc__db_status_t shadowed_presence;
5027
5028 svn_pool_clear(iterpool);
5029
5030 dst_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
5031
5032 if (!svn_sqlite__column_is_null(stmt, 2))
5033 shadowed_presence = svn_sqlite__column_token(stmt, 2, presence_map);
5034 else
5035 shadowed_presence = svn_wc__db_status_not_present;
5036
5037 if (shadowed_presence != svn_wc__db_status_normal
5038 && shadowed_presence != svn_wc__db_status_incomplete)
5039 {
5040 err = svn_sqlite__get_statement(&stmt2, wcroot->sdb,
5041 STMT_DELETE_NODE);
5042 }
5043 else
5044 {
5045 err =svn_sqlite__get_statement(&stmt2, wcroot->sdb,
5046 STMT_REPLACE_WITH_BASE_DELETED);
5047 }
5048
5049 if (!err)
5050 err = svn_sqlite__bindf(stmt2, "isd", wcroot->wc_id, dst_relpath,
5051 dst_op_depth);
5052
5053 if (!err)
5054 err = svn_sqlite__step_done(stmt2);
5055
5056 /* stmt2 is reset (never modified or by step_done) */
5057 if (err)
5058 break;
5059
5060 /* Delete ACTUAL information about this node that we just deleted */
5061 err = clear_or_remove_actual(wcroot, dst_relpath, dst_op_depth,
5062 scratch_pool);
5063
5064 if (err)
5065 break;
5066
5067 /* Retract base-delete for the node itself */
5068 err = db_retract_parent_delete(wcroot, dst_relpath, dst_op_depth,
5069 scratch_pool);
5070
5071 if (err)
5072 break;
5073
5074 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5075 }
5076 svn_pool_destroy(iterpool);
5077
5078 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
5079
5080 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5081
5082 if (conflict)
5083 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, dst_op_relpath /* ## */,
5084 conflict, scratch_pool));
5085
5086 return SVN_NO_ERROR;
5087 }
5088
5089 /* The txn body of svn_wc__db_op_handle_move_back */
5090 static svn_error_t *
handle_move_back(svn_boolean_t * moved_back,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,const char * moved_from_relpath,const svn_skel_t * work_items,apr_pool_t * scratch_pool)5091 handle_move_back(svn_boolean_t *moved_back,
5092 svn_wc__db_wcroot_t *wcroot,
5093 const char *local_relpath,
5094 const char *moved_from_relpath,
5095 const svn_skel_t *work_items,
5096 apr_pool_t *scratch_pool)
5097 {
5098 svn_sqlite__stmt_t *stmt;
5099 svn_wc__db_status_t status;
5100 svn_boolean_t op_root;
5101 svn_boolean_t have_more_work;
5102 int from_op_depth = 0;
5103 svn_boolean_t have_row;
5104 svn_boolean_t different = FALSE;
5105
5106 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5107
5108 SVN_ERR(svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL, NULL,
5109 NULL, NULL, NULL, NULL, NULL, NULL,
5110 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5111 &op_root, NULL, NULL, NULL,
5112 &have_more_work, NULL,
5113 wcroot, local_relpath,
5114 scratch_pool, scratch_pool));
5115
5116 if (status != svn_wc__db_status_added || !op_root)
5117 return SVN_NO_ERROR;
5118
5119 /* We have two cases here: BASE-move-back and WORKING-move-back */
5120 if (have_more_work)
5121 SVN_ERR(op_depth_of(&from_op_depth, wcroot,
5122 svn_relpath_dirname(local_relpath, scratch_pool)));
5123 else
5124 from_op_depth = 0;
5125
5126 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5127 STMT_SELECT_MOVED_BACK));
5128
5129 SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id,
5130 local_relpath,
5131 from_op_depth,
5132 relpath_depth(local_relpath)));
5133
5134 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5135
5136 SVN_ERR_ASSERT(have_row); /* We checked that the node is an op-root */
5137
5138 {
5139 svn_boolean_t moved_here = svn_sqlite__column_boolean(stmt, 9);
5140 const char *moved_to = svn_sqlite__column_text(stmt, 10, NULL);
5141
5142 if (!moved_here
5143 || !moved_to
5144 || strcmp(moved_to, moved_from_relpath))
5145 {
5146 different = TRUE;
5147 have_row = FALSE;
5148 }
5149 }
5150
5151 while (have_row)
5152 {
5153 svn_wc__db_status_t upper_status;
5154 svn_wc__db_status_t lower_status;
5155
5156 upper_status = svn_sqlite__column_token(stmt, 1, presence_map);
5157
5158 if (svn_sqlite__column_is_null(stmt, 5))
5159 {
5160 /* No lower layer replaced. */
5161 if (upper_status != svn_wc__db_status_not_present)
5162 {
5163 different = TRUE;
5164 break;
5165 }
5166 continue;
5167 }
5168
5169 lower_status = svn_sqlite__column_token(stmt, 5, presence_map);
5170
5171 if (upper_status != lower_status)
5172 {
5173 different = TRUE;
5174 break;
5175 }
5176
5177 if (upper_status == svn_wc__db_status_not_present
5178 || upper_status == svn_wc__db_status_excluded)
5179 {
5180 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5181 continue; /* Nothing to check */
5182 }
5183 else if (upper_status != svn_wc__db_status_normal)
5184 {
5185 /* Not a normal move. Mixed revision move? */
5186 different = TRUE;
5187 break;
5188 }
5189
5190 {
5191 const char *upper_repos_relpath;
5192 const char *lower_repos_relpath;
5193
5194 upper_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL);
5195 lower_repos_relpath = svn_sqlite__column_text(stmt, 7, NULL);
5196
5197 if (! upper_repos_relpath
5198 || strcmp(upper_repos_relpath, lower_repos_relpath))
5199 {
5200 different = TRUE;
5201 break;
5202 }
5203 }
5204
5205 {
5206 svn_revnum_t upper_rev;
5207 svn_revnum_t lower_rev;
5208
5209 upper_rev = svn_sqlite__column_revnum(stmt, 4);
5210 lower_rev = svn_sqlite__column_revnum(stmt, 8);
5211
5212 if (upper_rev != lower_rev)
5213 {
5214 different = TRUE;
5215 break;
5216 }
5217 }
5218
5219 {
5220 apr_int64_t upper_repos_id;
5221 apr_int64_t lower_repos_id;
5222
5223 upper_repos_id = svn_sqlite__column_int64(stmt, 2);
5224 lower_repos_id = svn_sqlite__column_int64(stmt, 6);
5225
5226 if (upper_repos_id != lower_repos_id)
5227 {
5228 different = TRUE;
5229 break;
5230 }
5231 }
5232
5233 /* Check moved_here? */
5234
5235 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5236 }
5237 SVN_ERR(svn_sqlite__reset(stmt));
5238
5239 if (! different)
5240 {
5241 /* Ok, we can now safely remove this complete move, because we
5242 determined that it 100% matches the layer below it. */
5243
5244 /* ### We could copy the recorded timestamps from the higher to the
5245 lower layer in an attempt to improve status performance, but
5246 generally these values should be the same anyway as it was
5247 a no-op move. */
5248 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5249 STMT_DELETE_WORKING_OP_DEPTH));
5250
5251 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
5252 local_relpath,
5253 relpath_depth(local_relpath)));
5254
5255 SVN_ERR(svn_sqlite__step_done(stmt));
5256
5257 if (moved_back)
5258 *moved_back = TRUE;
5259 }
5260
5261 return SVN_NO_ERROR;
5262 }
5263
5264 svn_error_t *
svn_wc__db_op_handle_move_back(svn_boolean_t * moved_back,svn_wc__db_t * db,const char * local_abspath,const char * moved_from_abspath,const svn_skel_t * work_items,apr_pool_t * scratch_pool)5265 svn_wc__db_op_handle_move_back(svn_boolean_t *moved_back,
5266 svn_wc__db_t *db,
5267 const char *local_abspath,
5268 const char *moved_from_abspath,
5269 const svn_skel_t *work_items,
5270 apr_pool_t *scratch_pool)
5271 {
5272 svn_wc__db_wcroot_t *wcroot;
5273 const char *local_relpath;
5274 const char *moved_from_relpath;
5275 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5276
5277 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5278 local_abspath,
5279 scratch_pool, scratch_pool));
5280 VERIFY_USABLE_WCROOT(wcroot);
5281
5282 if (moved_back)
5283 *moved_back = FALSE;
5284
5285 moved_from_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
5286 moved_from_abspath);
5287
5288 if (! local_relpath[0]
5289 || !moved_from_relpath)
5290 {
5291 /* WC-Roots can't be moved */
5292 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5293 return SVN_NO_ERROR;
5294 }
5295
5296 SVN_WC__DB_WITH_TXN(handle_move_back(moved_back, wcroot, local_relpath,
5297 moved_from_relpath, work_items,
5298 scratch_pool),
5299 wcroot);
5300
5301 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5302 scratch_pool));
5303
5304 return SVN_NO_ERROR;
5305 }
5306
5307
5308 /* The recursive implementation of svn_wc__db_op_copy_shadowed_layer.
5309 *
5310 * A non-zero MOVE_OP_DEPTH implies that the copy operation is part of
5311 * a move, and indicates the op-depth of the move destination op-root. */
5312 static svn_error_t *
db_op_copy_shadowed_layer(svn_wc__db_wcroot_t * src_wcroot,const char * src_relpath,int src_op_depth,svn_wc__db_wcroot_t * dst_wcroot,const char * dst_relpath,int dst_op_depth,int del_op_depth,apr_int64_t repos_id,const char * repos_relpath,svn_revnum_t revision,int move_op_depth,apr_pool_t * scratch_pool)5313 db_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot,
5314 const char *src_relpath,
5315 int src_op_depth,
5316 svn_wc__db_wcroot_t *dst_wcroot,
5317 const char *dst_relpath,
5318 int dst_op_depth,
5319 int del_op_depth,
5320 apr_int64_t repos_id,
5321 const char *repos_relpath,
5322 svn_revnum_t revision,
5323 int move_op_depth,
5324 apr_pool_t *scratch_pool)
5325 {
5326 const apr_array_header_t *children;
5327 apr_pool_t *iterpool;
5328 svn_wc__db_status_t status;
5329 svn_node_kind_t kind;
5330 svn_revnum_t node_revision;
5331 const char *node_repos_relpath;
5332 apr_int64_t node_repos_id;
5333 svn_sqlite__stmt_t *stmt;
5334 svn_wc__db_status_t dst_presence;
5335 int i;
5336
5337 {
5338 svn_error_t *err;
5339 err = svn_wc__db_depth_get_info(&status, &kind, &node_revision,
5340 &node_repos_relpath, &node_repos_id,
5341 NULL, NULL, NULL, NULL, NULL, NULL,
5342 NULL, NULL,
5343 src_wcroot, src_relpath, src_op_depth,
5344 scratch_pool, scratch_pool);
5345
5346 if (err)
5347 {
5348 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5349 return svn_error_trace(err);
5350
5351 svn_error_clear(err);
5352 return SVN_NO_ERROR; /* There is no shadowed node at src_op_depth */
5353 }
5354 }
5355
5356 if (src_op_depth == 0)
5357 {
5358 /* If the node is switched or has a different revision then its parent
5359 we shouldn't copy it. (We can't as we would have to insert it at
5360 an unshadowed depth) */
5361 if (status == svn_wc__db_status_not_present
5362 || status == svn_wc__db_status_excluded
5363 || status == svn_wc__db_status_server_excluded
5364 || node_revision != revision
5365 || node_repos_id != repos_id
5366 || strcmp(node_repos_relpath, repos_relpath))
5367 {
5368 /* Add a not-present node in the destination wcroot */
5369 struct insert_working_baton_t iwb;
5370 const char *repos_root_url;
5371 const char *repos_uuid;
5372
5373 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
5374 src_wcroot, node_repos_id,
5375 scratch_pool));
5376
5377 SVN_ERR(create_repos_id(&node_repos_id, repos_root_url, repos_uuid,
5378 dst_wcroot->sdb, scratch_pool));
5379
5380 blank_iwb(&iwb);
5381
5382 iwb.op_depth = dst_op_depth;
5383 if (status != svn_wc__db_status_excluded)
5384 iwb.presence = svn_wc__db_status_not_present;
5385 else
5386 iwb.presence = svn_wc__db_status_excluded;
5387
5388 iwb.kind = kind;
5389
5390 iwb.original_repos_id = node_repos_id;
5391 iwb.original_revnum = node_revision;
5392 iwb.original_repos_relpath = node_repos_relpath;
5393
5394 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5395 scratch_pool));
5396
5397 return SVN_NO_ERROR;
5398 }
5399 }
5400
5401 iterpool = svn_pool_create(scratch_pool);
5402
5403 switch (status)
5404 {
5405 case svn_wc__db_status_normal:
5406 case svn_wc__db_status_added:
5407 case svn_wc__db_status_moved_here:
5408 case svn_wc__db_status_copied:
5409 dst_presence = svn_wc__db_status_normal;
5410 break;
5411 case svn_wc__db_status_deleted:
5412 case svn_wc__db_status_not_present:
5413 dst_presence = svn_wc__db_status_not_present;
5414 break;
5415 case svn_wc__db_status_excluded:
5416 dst_presence = svn_wc__db_status_excluded;
5417 break;
5418 case svn_wc__db_status_server_excluded:
5419 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5420 _("Cannot copy '%s' excluded by server"),
5421 path_for_error_message(src_wcroot,
5422 src_relpath,
5423 scratch_pool));
5424 default:
5425 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5426 _("Cannot handle status of '%s'"),
5427 path_for_error_message(src_wcroot,
5428 src_relpath,
5429 scratch_pool));
5430 }
5431
5432 if (dst_presence == svn_wc__db_status_normal
5433 && src_wcroot == dst_wcroot) /* ### Remove limitation */
5434 {
5435 SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
5436 STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH));
5437
5438 SVN_ERR(svn_sqlite__bindf(stmt, "issdstd",
5439 src_wcroot->wc_id, src_relpath,
5440 dst_relpath,
5441 dst_op_depth,
5442 svn_relpath_dirname(dst_relpath, iterpool),
5443 presence_map, dst_presence,
5444 src_op_depth));
5445
5446 /* moved_here */
5447 if (dst_op_depth == move_op_depth)
5448 SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
5449
5450 SVN_ERR(svn_sqlite__step_done(stmt));
5451
5452 {
5453 /* And mark it deleted to allow proper shadowing */
5454 struct insert_working_baton_t iwb;
5455
5456 blank_iwb(&iwb);
5457
5458 iwb.op_depth = del_op_depth;
5459 iwb.presence = svn_wc__db_status_base_deleted;
5460
5461 iwb.kind = kind;
5462
5463 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5464 scratch_pool));
5465 }
5466 }
5467 else
5468 {
5469 struct insert_working_baton_t iwb;
5470 if (dst_presence == svn_wc__db_status_normal) /* Fallback for multi-db */
5471 dst_presence = svn_wc__db_status_not_present;
5472
5473 /* And mark it deleted to allow proper shadowing */
5474
5475 blank_iwb(&iwb);
5476
5477 iwb.op_depth = dst_op_depth;
5478 iwb.presence = dst_presence;
5479 iwb.kind = kind;
5480
5481 SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5482 scratch_pool));
5483 }
5484
5485 if (dst_presence == svn_wc__db_status_not_present)
5486 {
5487 /* Don't create descendants of a not present node! */
5488
5489 /* This code is currently still triggered by copying deleted nodes
5490 between separate working copies. See ### comment above. */
5491
5492 svn_pool_destroy(iterpool);
5493 return SVN_NO_ERROR;
5494 }
5495
5496 SVN_ERR(gather_children(&children, src_wcroot, src_relpath,
5497 STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth,
5498 scratch_pool, iterpool));
5499
5500 for (i = 0; i < children->nelts; i++)
5501 {
5502 const char *name = APR_ARRAY_IDX(children, i, const char *);
5503 const char *child_src_relpath;
5504 const char *child_dst_relpath;
5505 const char *child_repos_relpath = NULL;
5506
5507 svn_pool_clear(iterpool);
5508 child_src_relpath = svn_relpath_join(src_relpath, name, iterpool);
5509 child_dst_relpath = svn_relpath_join(dst_relpath, name, iterpool);
5510
5511 if (repos_relpath)
5512 child_repos_relpath = svn_relpath_join(repos_relpath, name, iterpool);
5513
5514 SVN_ERR(db_op_copy_shadowed_layer(
5515 src_wcroot, child_src_relpath, src_op_depth,
5516 dst_wcroot, child_dst_relpath, dst_op_depth,
5517 del_op_depth,
5518 repos_id, child_repos_relpath, revision,
5519 move_op_depth, scratch_pool));
5520 }
5521
5522 svn_pool_destroy(iterpool);
5523
5524 return SVN_NO_ERROR;
5525 }
5526
5527 /* Helper for svn_wc__db_op_copy_shadowed_layer(). */
5528 static svn_error_t *
op_copy_shadowed_layer_txn(svn_wc__db_wcroot_t * wcroot,struct op_copy_baton * ocb,apr_pool_t * scratch_pool)5529 op_copy_shadowed_layer_txn(svn_wc__db_wcroot_t *wcroot,
5530 struct op_copy_baton *ocb,
5531 apr_pool_t *scratch_pool)
5532 {
5533 const char *src_parent_relpath;
5534 const char *dst_parent_relpath;
5535 int src_op_depth;
5536 int dst_op_depth;
5537 int del_op_depth;
5538 const char *repos_relpath = NULL;
5539 apr_int64_t repos_id = INVALID_REPOS_ID;
5540 svn_revnum_t revision = SVN_INVALID_REVNUM;
5541
5542 if (wcroot != ocb->dst_wcroot)
5543 {
5544 /* Source and destination databases differ; so also start a lock
5545 in the destination database, by calling ourself in an extra lock. */
5546
5547 SVN_WC__DB_WITH_TXN(op_copy_shadowed_layer_txn(ocb->dst_wcroot, ocb,
5548 scratch_pool),
5549 ocb->dst_wcroot);
5550
5551 return SVN_NO_ERROR;
5552 }
5553
5554 /* From this point we can assume a lock in the src and dst databases */
5555
5556
5557 /* src_relpath and dst_relpath can't be wcroot as we need their parents */
5558 SVN_ERR_ASSERT(*ocb->src_relpath && *ocb->dst_relpath);
5559
5560 src_parent_relpath = svn_relpath_dirname(ocb->src_relpath, scratch_pool);
5561 dst_parent_relpath = svn_relpath_dirname(ocb->dst_relpath, scratch_pool);
5562
5563 /* src_parent must be status normal or added; get its op-depth */
5564 SVN_ERR(op_depth_of(&src_op_depth, ocb->src_wcroot, src_parent_relpath));
5565
5566 /* dst_parent must be status added; get its op-depth */
5567 SVN_ERR(op_depth_of(&dst_op_depth, ocb->dst_wcroot, dst_parent_relpath));
5568
5569 del_op_depth = relpath_depth(ocb->dst_relpath);
5570
5571 /* Get some information from the parent */
5572 SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, &revision, &repos_relpath,
5573 &repos_id, NULL, NULL, NULL, NULL, NULL,
5574 NULL, NULL, NULL,
5575 ocb->src_wcroot,
5576 src_parent_relpath, src_op_depth,
5577 scratch_pool, scratch_pool));
5578
5579 if (repos_relpath == NULL)
5580 {
5581 /* The node is a local addition and has no shadowed information */
5582 return SVN_NO_ERROR;
5583 }
5584
5585 /* And calculate the child repos relpath */
5586 repos_relpath = svn_relpath_join(repos_relpath,
5587 svn_relpath_basename(ocb->src_relpath,
5588 NULL),
5589 scratch_pool);
5590
5591 SVN_ERR(db_op_copy_shadowed_layer(
5592 ocb->src_wcroot, ocb->src_relpath, src_op_depth,
5593 ocb->dst_wcroot, ocb->dst_relpath, dst_op_depth,
5594 del_op_depth,
5595 repos_id, repos_relpath, revision,
5596 (ocb->is_move ? dst_op_depth : 0),
5597 scratch_pool));
5598
5599 return SVN_NO_ERROR;
5600 }
5601
5602 svn_error_t *
svn_wc__db_op_copy_shadowed_layer(svn_wc__db_t * db,const char * src_abspath,const char * dst_abspath,svn_boolean_t is_move,apr_pool_t * scratch_pool)5603 svn_wc__db_op_copy_shadowed_layer(svn_wc__db_t *db,
5604 const char *src_abspath,
5605 const char *dst_abspath,
5606 svn_boolean_t is_move,
5607 apr_pool_t *scratch_pool)
5608 {
5609 struct op_copy_baton ocb = {0};
5610
5611 SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
5612 SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
5613
5614 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
5615 &ocb.src_relpath, db,
5616 src_abspath,
5617 scratch_pool, scratch_pool));
5618 VERIFY_USABLE_WCROOT(ocb.src_wcroot);
5619
5620 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
5621 &ocb.dst_relpath,
5622 db, dst_abspath,
5623 scratch_pool, scratch_pool));
5624 VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
5625
5626 ocb.is_move = is_move;
5627 ocb.dst_op_root_relpath = NULL; /* not used by op_copy_shadowed_layer_txn */
5628
5629 ocb.work_items = NULL;
5630
5631 /* Call with the sdb in src_wcroot. It might call itself again to
5632 also obtain a lock in dst_wcroot */
5633 SVN_WC__DB_WITH_TXN(op_copy_shadowed_layer_txn(ocb.src_wcroot, &ocb,
5634 scratch_pool),
5635 ocb.src_wcroot);
5636
5637 return SVN_NO_ERROR;
5638 }
5639
5640
5641 /* If there are any server-excluded base nodes then the copy must fail
5642 as it's not possible to commit such a copy.
5643 Return an error if there are any server-excluded nodes. */
5644 static svn_error_t *
catch_copy_of_server_excluded(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)5645 catch_copy_of_server_excluded(svn_wc__db_wcroot_t *wcroot,
5646 const char *local_relpath,
5647 apr_pool_t *scratch_pool)
5648 {
5649 svn_sqlite__stmt_t *stmt;
5650 svn_boolean_t have_row;
5651 const char *server_excluded_relpath;
5652
5653 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5654 STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
5655 SVN_ERR(svn_sqlite__bindf(stmt, "is",
5656 wcroot->wc_id,
5657 local_relpath));
5658 SVN_ERR(svn_sqlite__step(&have_row, stmt));
5659 if (have_row)
5660 server_excluded_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
5661 SVN_ERR(svn_sqlite__reset(stmt));
5662 if (have_row)
5663 return svn_error_createf(SVN_ERR_AUTHZ_UNREADABLE, NULL,
5664 _("Cannot copy '%s' excluded by server"),
5665 path_for_error_message(wcroot,
5666 server_excluded_relpath,
5667 scratch_pool));
5668
5669 return SVN_NO_ERROR;
5670 }
5671
5672
5673 svn_error_t *
svn_wc__db_op_copy_dir(svn_wc__db_t * db,const char * local_abspath,const apr_hash_t * props,svn_revnum_t changed_rev,apr_time_t changed_date,const char * changed_author,const char * original_repos_relpath,const char * original_root_url,const char * original_uuid,svn_revnum_t original_revision,const apr_array_header_t * children,svn_depth_t depth,svn_boolean_t is_move,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)5674 svn_wc__db_op_copy_dir(svn_wc__db_t *db,
5675 const char *local_abspath,
5676 const apr_hash_t *props,
5677 svn_revnum_t changed_rev,
5678 apr_time_t changed_date,
5679 const char *changed_author,
5680 const char *original_repos_relpath,
5681 const char *original_root_url,
5682 const char *original_uuid,
5683 svn_revnum_t original_revision,
5684 const apr_array_header_t *children,
5685 svn_depth_t depth,
5686 svn_boolean_t is_move,
5687 const svn_skel_t *conflict,
5688 const svn_skel_t *work_items,
5689 apr_pool_t *scratch_pool)
5690 {
5691 svn_wc__db_wcroot_t *wcroot;
5692 const char *local_relpath;
5693 insert_working_baton_t iwb;
5694 int parent_op_depth;
5695
5696 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5697 SVN_ERR_ASSERT(props != NULL);
5698 /* ### any assertions for CHANGED_* ? */
5699 /* ### any assertions for ORIGINAL_* ? */
5700 #if 0
5701 SVN_ERR_ASSERT(children != NULL);
5702 #endif
5703
5704 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5705 local_abspath, scratch_pool, scratch_pool));
5706 VERIFY_USABLE_WCROOT(wcroot);
5707
5708 blank_iwb(&iwb);
5709
5710 iwb.presence = svn_wc__db_status_normal;
5711 iwb.kind = svn_node_dir;
5712
5713 if (original_root_url != NULL)
5714 {
5715 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5716 original_root_url, original_uuid,
5717 wcroot->sdb, scratch_pool));
5718 iwb.original_repos_relpath = original_repos_relpath;
5719 iwb.original_revnum = original_revision;
5720
5721 iwb.props = props;
5722 iwb.changed_rev = changed_rev;
5723 iwb.changed_date = changed_date;
5724 iwb.changed_author = changed_author;
5725 }
5726
5727 /* ### Should we do this inside the transaction? */
5728 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5729 &parent_op_depth, iwb.original_repos_id,
5730 original_repos_relpath, original_revision,
5731 wcroot, local_relpath, scratch_pool));
5732
5733 iwb.children = children;
5734 iwb.depth = depth;
5735 iwb.moved_here = is_move && (parent_op_depth == 0 ||
5736 iwb.op_depth == parent_op_depth);
5737
5738 iwb.work_items = work_items;
5739 iwb.conflict = conflict;
5740
5741 SVN_WC__DB_WITH_TXN(
5742 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5743 wcroot);
5744 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
5745
5746 return SVN_NO_ERROR;
5747 }
5748
5749
5750 svn_error_t *
svn_wc__db_op_copy_file(svn_wc__db_t * db,const char * local_abspath,const apr_hash_t * props,svn_revnum_t changed_rev,apr_time_t changed_date,const char * changed_author,const char * original_repos_relpath,const char * original_root_url,const char * original_uuid,svn_revnum_t original_revision,const svn_checksum_t * checksum,svn_boolean_t update_actual_props,const apr_hash_t * new_actual_props,svn_boolean_t is_move,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)5751 svn_wc__db_op_copy_file(svn_wc__db_t *db,
5752 const char *local_abspath,
5753 const apr_hash_t *props,
5754 svn_revnum_t changed_rev,
5755 apr_time_t changed_date,
5756 const char *changed_author,
5757 const char *original_repos_relpath,
5758 const char *original_root_url,
5759 const char *original_uuid,
5760 svn_revnum_t original_revision,
5761 const svn_checksum_t *checksum,
5762 svn_boolean_t update_actual_props,
5763 const apr_hash_t *new_actual_props,
5764 svn_boolean_t is_move,
5765 const svn_skel_t *conflict,
5766 const svn_skel_t *work_items,
5767 apr_pool_t *scratch_pool)
5768 {
5769 svn_wc__db_wcroot_t *wcroot;
5770 const char *local_relpath;
5771 insert_working_baton_t iwb;
5772 int parent_op_depth;
5773
5774 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5775 SVN_ERR_ASSERT(props != NULL);
5776 /* ### any assertions for CHANGED_* ? */
5777 SVN_ERR_ASSERT((! original_repos_relpath && ! original_root_url
5778 && ! original_uuid && ! checksum
5779 && original_revision == SVN_INVALID_REVNUM)
5780 || (original_repos_relpath && original_root_url
5781 && original_uuid && checksum
5782 && original_revision != SVN_INVALID_REVNUM));
5783
5784 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5785 local_abspath, scratch_pool, scratch_pool));
5786 VERIFY_USABLE_WCROOT(wcroot);
5787
5788 blank_iwb(&iwb);
5789
5790 iwb.presence = svn_wc__db_status_normal;
5791 iwb.kind = svn_node_file;
5792
5793 if (original_root_url != NULL)
5794 {
5795 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5796 original_root_url, original_uuid,
5797 wcroot->sdb, scratch_pool));
5798 iwb.original_repos_relpath = original_repos_relpath;
5799 iwb.original_revnum = original_revision;
5800
5801 iwb.props = props;
5802 iwb.changed_rev = changed_rev;
5803 iwb.changed_date = changed_date;
5804 iwb.changed_author = changed_author;
5805 }
5806
5807 /* ### Should we do this inside the transaction? */
5808 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5809 &parent_op_depth, iwb.original_repos_id,
5810 original_repos_relpath, original_revision,
5811 wcroot, local_relpath, scratch_pool));
5812
5813 iwb.checksum = checksum;
5814 iwb.moved_here = is_move && (parent_op_depth == 0 ||
5815 iwb.op_depth == parent_op_depth);
5816
5817 if (update_actual_props)
5818 {
5819 iwb.update_actual_props = update_actual_props;
5820 iwb.new_actual_props = new_actual_props;
5821 }
5822
5823 iwb.work_items = work_items;
5824 iwb.conflict = conflict;
5825
5826 SVN_WC__DB_WITH_TXN(
5827 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5828 wcroot);
5829 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5830
5831 return SVN_NO_ERROR;
5832 }
5833
5834
5835 svn_error_t *
svn_wc__db_op_copy_symlink(svn_wc__db_t * db,const char * local_abspath,const apr_hash_t * props,svn_revnum_t changed_rev,apr_time_t changed_date,const char * changed_author,const char * original_repos_relpath,const char * original_root_url,const char * original_uuid,svn_revnum_t original_revision,const char * target,svn_boolean_t is_move,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)5836 svn_wc__db_op_copy_symlink(svn_wc__db_t *db,
5837 const char *local_abspath,
5838 const apr_hash_t *props,
5839 svn_revnum_t changed_rev,
5840 apr_time_t changed_date,
5841 const char *changed_author,
5842 const char *original_repos_relpath,
5843 const char *original_root_url,
5844 const char *original_uuid,
5845 svn_revnum_t original_revision,
5846 const char *target,
5847 svn_boolean_t is_move,
5848 const svn_skel_t *conflict,
5849 const svn_skel_t *work_items,
5850 apr_pool_t *scratch_pool)
5851 {
5852 svn_wc__db_wcroot_t *wcroot;
5853 const char *local_relpath;
5854 insert_working_baton_t iwb;
5855 int parent_op_depth;
5856
5857 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5858 SVN_ERR_ASSERT(props != NULL);
5859 /* ### any assertions for CHANGED_* ? */
5860 /* ### any assertions for ORIGINAL_* ? */
5861 SVN_ERR_ASSERT(target != NULL);
5862
5863 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5864 local_abspath, scratch_pool, scratch_pool));
5865 VERIFY_USABLE_WCROOT(wcroot);
5866
5867 blank_iwb(&iwb);
5868
5869 iwb.presence = svn_wc__db_status_normal;
5870 iwb.kind = svn_node_symlink;
5871
5872
5873 if (original_root_url != NULL)
5874 {
5875 SVN_ERR(create_repos_id(&iwb.original_repos_id,
5876 original_root_url, original_uuid,
5877 wcroot->sdb, scratch_pool));
5878 iwb.original_repos_relpath = original_repos_relpath;
5879 iwb.original_revnum = original_revision;
5880
5881 iwb.props = props;
5882 iwb.changed_rev = changed_rev;
5883 iwb.changed_date = changed_date;
5884 iwb.changed_author = changed_author;
5885 }
5886
5887 /* ### Should we do this inside the transaction? */
5888 SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5889 &parent_op_depth, iwb.original_repos_id,
5890 original_repos_relpath, original_revision,
5891 wcroot, local_relpath, scratch_pool));
5892
5893 iwb.target = target;
5894 iwb.moved_here = is_move && (parent_op_depth == 0 ||
5895 iwb.op_depth == parent_op_depth);
5896
5897 iwb.work_items = work_items;
5898 iwb.conflict = conflict;
5899
5900 SVN_WC__DB_WITH_TXN(
5901 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5902 wcroot);
5903 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5904
5905 return SVN_NO_ERROR;
5906 }
5907
5908
5909 svn_error_t *
svn_wc__db_op_add_directory(svn_wc__db_t * db,const char * local_abspath,const apr_hash_t * props,const svn_skel_t * work_items,apr_pool_t * scratch_pool)5910 svn_wc__db_op_add_directory(svn_wc__db_t *db,
5911 const char *local_abspath,
5912 const apr_hash_t *props,
5913 const svn_skel_t *work_items,
5914 apr_pool_t *scratch_pool)
5915 {
5916 svn_wc__db_wcroot_t *wcroot;
5917 const char *local_relpath;
5918 const char *dir_abspath;
5919 const char *name;
5920 insert_working_baton_t iwb;
5921
5922 /* Resolve wcroot via parent directory to avoid obstruction handling */
5923 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5924 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5925
5926 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5927 dir_abspath, scratch_pool, scratch_pool));
5928 VERIFY_USABLE_WCROOT(wcroot);
5929
5930 blank_iwb(&iwb);
5931
5932 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5933 iwb.presence = svn_wc__db_status_normal;
5934 iwb.kind = svn_node_dir;
5935 iwb.op_depth = relpath_depth(local_relpath);
5936 if (props && apr_hash_count((apr_hash_t *)props))
5937 {
5938 iwb.update_actual_props = TRUE;
5939 iwb.new_actual_props = props;
5940 }
5941
5942 iwb.work_items = work_items;
5943
5944 SVN_WC__DB_WITH_TXN(
5945 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5946 wcroot);
5947 /* Use depth infinity to make sure we have no invalid cached information
5948 * about children of this dir. */
5949 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5950 scratch_pool));
5951
5952 return SVN_NO_ERROR;
5953 }
5954
5955
5956 svn_error_t *
svn_wc__db_op_add_file(svn_wc__db_t * db,const char * local_abspath,const apr_hash_t * props,const svn_skel_t * work_items,apr_pool_t * scratch_pool)5957 svn_wc__db_op_add_file(svn_wc__db_t *db,
5958 const char *local_abspath,
5959 const apr_hash_t *props,
5960 const svn_skel_t *work_items,
5961 apr_pool_t *scratch_pool)
5962 {
5963 svn_wc__db_wcroot_t *wcroot;
5964 const char *local_relpath;
5965 insert_working_baton_t iwb;
5966 const char *dir_abspath;
5967 const char *name;
5968
5969 /* Resolve wcroot via parent directory to avoid obstruction handling */
5970 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5971 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5972
5973 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5974 dir_abspath, scratch_pool, scratch_pool));
5975 VERIFY_USABLE_WCROOT(wcroot);
5976
5977 blank_iwb(&iwb);
5978
5979 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5980 iwb.presence = svn_wc__db_status_normal;
5981 iwb.kind = svn_node_file;
5982 iwb.op_depth = relpath_depth(local_relpath);
5983 if (props && apr_hash_count((apr_hash_t *)props))
5984 {
5985 iwb.update_actual_props = TRUE;
5986 iwb.new_actual_props = props;
5987 }
5988
5989 iwb.work_items = work_items;
5990
5991 SVN_WC__DB_WITH_TXN(
5992 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5993 wcroot);
5994 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5995
5996 return SVN_NO_ERROR;
5997 }
5998
5999
6000 svn_error_t *
svn_wc__db_op_add_symlink(svn_wc__db_t * db,const char * local_abspath,const char * target,const apr_hash_t * props,const svn_skel_t * work_items,apr_pool_t * scratch_pool)6001 svn_wc__db_op_add_symlink(svn_wc__db_t *db,
6002 const char *local_abspath,
6003 const char *target,
6004 const apr_hash_t *props,
6005 const svn_skel_t *work_items,
6006 apr_pool_t *scratch_pool)
6007 {
6008 svn_wc__db_wcroot_t *wcroot;
6009 const char *local_relpath;
6010 insert_working_baton_t iwb;
6011 const char *dir_abspath;
6012 const char *name;
6013
6014 /* Resolve wcroot via parent directory to avoid obstruction handling */
6015 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6016 SVN_ERR_ASSERT(target != NULL);
6017
6018 svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
6019
6020 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6021 dir_abspath, scratch_pool, scratch_pool));
6022
6023 VERIFY_USABLE_WCROOT(wcroot);
6024
6025 blank_iwb(&iwb);
6026
6027 local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
6028 iwb.presence = svn_wc__db_status_normal;
6029 iwb.kind = svn_node_symlink;
6030 iwb.op_depth = relpath_depth(local_relpath);
6031 if (props && apr_hash_count((apr_hash_t *)props))
6032 {
6033 iwb.update_actual_props = TRUE;
6034 iwb.new_actual_props = props;
6035 }
6036
6037 iwb.target = target;
6038
6039 iwb.work_items = work_items;
6040
6041 SVN_WC__DB_WITH_TXN(
6042 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
6043 wcroot);
6044 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6045
6046 return SVN_NO_ERROR;
6047 }
6048
6049 /* Record RECORDED_SIZE and RECORDED_TIME into top layer in NODES */
6050 static svn_error_t *
db_record_fileinfo(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_int64_t recorded_size,apr_int64_t recorded_time,apr_pool_t * scratch_pool)6051 db_record_fileinfo(svn_wc__db_wcroot_t *wcroot,
6052 const char *local_relpath,
6053 apr_int64_t recorded_size,
6054 apr_int64_t recorded_time,
6055 apr_pool_t *scratch_pool)
6056 {
6057 svn_sqlite__stmt_t *stmt;
6058 int affected_rows;
6059
6060 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6061 STMT_UPDATE_NODE_FILEINFO));
6062 SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
6063 recorded_size, recorded_time));
6064 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6065
6066 SVN_ERR_ASSERT(affected_rows == 1);
6067
6068 return SVN_NO_ERROR;
6069 }
6070
6071
6072 svn_error_t *
svn_wc__db_global_record_fileinfo(svn_wc__db_t * db,const char * local_abspath,svn_filesize_t recorded_size,apr_time_t recorded_time,apr_pool_t * scratch_pool)6073 svn_wc__db_global_record_fileinfo(svn_wc__db_t *db,
6074 const char *local_abspath,
6075 svn_filesize_t recorded_size,
6076 apr_time_t recorded_time,
6077 apr_pool_t *scratch_pool)
6078 {
6079 svn_wc__db_wcroot_t *wcroot;
6080 const char *local_relpath;
6081
6082 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6083
6084 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6085 local_abspath, scratch_pool, scratch_pool));
6086 VERIFY_USABLE_WCROOT(wcroot);
6087
6088 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
6089 recorded_size, recorded_time, scratch_pool));
6090
6091 /* We *totally* monkeyed the entries. Toss 'em. */
6092 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6093
6094 return SVN_NO_ERROR;
6095 }
6096
6097
6098 /* Set the ACTUAL_NODE properties column for (WC_ID, LOCAL_RELPATH) to
6099 * PROPS.
6100 *
6101 * Note: PROPS=NULL means the actual props are the same as the pristine
6102 * props; to indicate no properties when the pristine has some props,
6103 * PROPS must be an empty hash. */
6104 static svn_error_t *
set_actual_props(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_hash_t * props,apr_pool_t * scratch_pool)6105 set_actual_props(svn_wc__db_wcroot_t *wcroot,
6106 const char *local_relpath,
6107 apr_hash_t *props,
6108 apr_pool_t *scratch_pool)
6109 {
6110 svn_sqlite__stmt_t *stmt;
6111 int affected_rows;
6112
6113 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6114 STMT_UPDATE_ACTUAL_PROPS));
6115 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6116 SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
6117 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6118
6119 if (affected_rows == 1 || !props)
6120 {
6121 /* Perhaps the entire ACTUAL record is unneeded now? */
6122 if (!props && affected_rows)
6123 {
6124 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6125 STMT_DELETE_ACTUAL_EMPTY));
6126 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6127 SVN_ERR(svn_sqlite__step_done(stmt));
6128 }
6129
6130 return SVN_NO_ERROR; /* We are done */
6131 }
6132
6133 /* We have to insert a row in ACTUAL */
6134
6135 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6136 STMT_INSERT_ACTUAL_PROPS));
6137 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6138 if (*local_relpath != '\0')
6139 SVN_ERR(svn_sqlite__bind_text(stmt, 3,
6140 svn_relpath_dirname(local_relpath,
6141 scratch_pool)));
6142 SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
6143 return svn_error_trace(svn_sqlite__step_done(stmt));
6144 }
6145
6146 svn_error_t *
svn_wc__db_op_set_props_internal(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_hash_t * props,svn_boolean_t clear_recorded_info,apr_pool_t * scratch_pool)6147 svn_wc__db_op_set_props_internal(svn_wc__db_wcroot_t *wcroot,
6148 const char *local_relpath,
6149 apr_hash_t *props,
6150 svn_boolean_t clear_recorded_info,
6151 apr_pool_t *scratch_pool)
6152 {
6153 SVN_ERR(set_actual_props(wcroot, local_relpath, props, scratch_pool));
6154
6155 if (clear_recorded_info)
6156 {
6157 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
6158 SVN_INVALID_FILESIZE, 0,
6159 scratch_pool));
6160 }
6161
6162 return SVN_NO_ERROR;
6163 }
6164
6165 /* The body of svn_wc__db_op_set_props().
6166
6167 Set the 'properties' column in the 'ACTUAL_NODE' table to BATON->props.
6168 Create an entry in the ACTUAL table for the node if it does not yet
6169 have one.
6170 To specify no properties, BATON->props must be an empty hash, not NULL.
6171 BATON is of type 'struct set_props_baton_t'.
6172 */
6173 static svn_error_t *
set_props_txn(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_hash_t * props,svn_boolean_t clear_recorded_info,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)6174 set_props_txn(svn_wc__db_wcroot_t *wcroot,
6175 const char *local_relpath,
6176 apr_hash_t *props,
6177 svn_boolean_t clear_recorded_info,
6178 const svn_skel_t *conflict,
6179 const svn_skel_t *work_items,
6180 apr_pool_t *scratch_pool)
6181 {
6182 apr_hash_t *pristine_props;
6183
6184 /* Check if the props are modified. If no changes, then wipe out the
6185 ACTUAL props. PRISTINE_PROPS==NULL means that any
6186 ACTUAL props are okay as provided, so go ahead and set them. */
6187 SVN_ERR(db_read_pristine_props(&pristine_props, wcroot, local_relpath, FALSE,
6188 scratch_pool, scratch_pool));
6189 if (props && pristine_props)
6190 {
6191 apr_array_header_t *prop_diffs;
6192
6193 SVN_ERR(svn_prop_diffs(&prop_diffs, props, pristine_props,
6194 scratch_pool));
6195 if (prop_diffs->nelts == 0)
6196 props = NULL;
6197 }
6198
6199 SVN_ERR(svn_wc__db_op_set_props_internal(wcroot, local_relpath, props,
6200 clear_recorded_info, scratch_pool));
6201
6202 /* And finally. */
6203 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6204 if (conflict)
6205 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6206 conflict, scratch_pool));
6207
6208 return SVN_NO_ERROR;
6209 }
6210
6211
6212 svn_error_t *
svn_wc__db_op_set_props(svn_wc__db_t * db,const char * local_abspath,apr_hash_t * props,svn_boolean_t clear_recorded_info,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)6213 svn_wc__db_op_set_props(svn_wc__db_t *db,
6214 const char *local_abspath,
6215 apr_hash_t *props,
6216 svn_boolean_t clear_recorded_info,
6217 const svn_skel_t *conflict,
6218 const svn_skel_t *work_items,
6219 apr_pool_t *scratch_pool)
6220 {
6221 svn_wc__db_wcroot_t *wcroot;
6222 const char *local_relpath;
6223
6224 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6225
6226 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6227 db, local_abspath, scratch_pool, scratch_pool));
6228 VERIFY_USABLE_WCROOT(wcroot);
6229
6230 SVN_WC__DB_WITH_TXN(set_props_txn(wcroot, local_relpath, props,
6231 clear_recorded_info, conflict, work_items,
6232 scratch_pool),
6233 wcroot);
6234 return SVN_NO_ERROR;
6235 }
6236
6237
6238 svn_error_t *
svn_wc__db_op_modified(svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)6239 svn_wc__db_op_modified(svn_wc__db_t *db,
6240 const char *local_abspath,
6241 apr_pool_t *scratch_pool)
6242 {
6243 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6244
6245 NOT_IMPLEMENTED();
6246 }
6247
6248 /* */
6249 static svn_error_t *
populate_targets_tree(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_depth_t depth,const apr_array_header_t * changelist_filter,apr_pool_t * scratch_pool)6250 populate_targets_tree(svn_wc__db_wcroot_t *wcroot,
6251 const char *local_relpath,
6252 svn_depth_t depth,
6253 const apr_array_header_t *changelist_filter,
6254 apr_pool_t *scratch_pool)
6255 {
6256 svn_sqlite__stmt_t *stmt;
6257 int affected_rows = 0;
6258 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6259 STMT_CREATE_TARGETS_LIST));
6260
6261 if (changelist_filter && changelist_filter->nelts > 0)
6262 {
6263 /* Iterate over the changelists, adding the nodes which match.
6264 Common case: we only have one changelist, so this only
6265 happens once. */
6266 int i;
6267 int stmt_idx;
6268
6269 switch (depth)
6270 {
6271 case svn_depth_empty:
6272 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST;
6273 break;
6274
6275 case svn_depth_files:
6276 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES;
6277 break;
6278
6279 case svn_depth_immediates:
6280 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES;
6281 break;
6282
6283 case svn_depth_infinity:
6284 stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY;
6285 break;
6286
6287 default:
6288 /* We don't know how to handle unknown or exclude. */
6289 SVN_ERR_MALFUNCTION();
6290 break;
6291 }
6292
6293 for (i = 0; i < changelist_filter->nelts; i++)
6294 {
6295 int sub_affected;
6296 const char *changelist = APR_ARRAY_IDX(changelist_filter, i,
6297 const char *);
6298
6299 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6300 STMT_INSERT_TARGET_WITH_CHANGELIST));
6301 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
6302 local_relpath, changelist));
6303 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6304
6305 /* If the root is matched by the changelist, we don't have to match
6306 the children. As that tells us the root is a file */
6307 if (!sub_affected && depth > svn_depth_empty)
6308 {
6309 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
6310 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
6311 local_relpath, changelist));
6312 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6313 }
6314
6315 affected_rows += sub_affected;
6316 }
6317 }
6318 else /* No changelist filtering */
6319 {
6320 int stmt_idx;
6321 int sub_affected;
6322
6323 switch (depth)
6324 {
6325 case svn_depth_empty:
6326 stmt_idx = STMT_INSERT_TARGET;
6327 break;
6328
6329 case svn_depth_files:
6330 stmt_idx = STMT_INSERT_TARGET_DEPTH_FILES;
6331 break;
6332
6333 case svn_depth_immediates:
6334 stmt_idx = STMT_INSERT_TARGET_DEPTH_IMMEDIATES;
6335 break;
6336
6337 case svn_depth_infinity:
6338 stmt_idx = STMT_INSERT_TARGET_DEPTH_INFINITY;
6339 break;
6340
6341 default:
6342 /* We don't know how to handle unknown or exclude. */
6343 SVN_ERR_MALFUNCTION();
6344 break;
6345 }
6346
6347 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6348 STMT_INSERT_TARGET));
6349 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6350 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6351 affected_rows += sub_affected;
6352
6353 if (depth > svn_depth_empty)
6354 {
6355 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
6356 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6357 SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6358 affected_rows += sub_affected;
6359 }
6360 }
6361
6362 /* Does the target exist? */
6363 if (affected_rows == 0)
6364 {
6365 svn_boolean_t exists;
6366 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
6367
6368 if (!exists)
6369 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6370 _("The node '%s' was not found."),
6371 path_for_error_message(wcroot,
6372 local_relpath,
6373 scratch_pool));
6374 }
6375
6376 return SVN_NO_ERROR;
6377 }
6378
6379
6380 #if 0
6381 static svn_error_t *
6382 dump_targets(svn_wc__db_wcroot_t *wcroot,
6383 apr_pool_t *scratch_pool)
6384 {
6385 svn_sqlite__stmt_t *stmt;
6386 svn_boolean_t have_row;
6387
6388 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6389 STMT_SELECT_TARGETS));
6390 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6391 while (have_row)
6392 {
6393 const char *target = svn_sqlite__column_text(stmt, 0, NULL);
6394 SVN_DBG(("Target: '%s'\n", target));
6395 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6396 }
6397
6398 SVN_ERR(svn_sqlite__reset(stmt));
6399
6400 return SVN_NO_ERROR;
6401 }
6402 #endif
6403
6404
6405 struct set_changelist_baton_t
6406 {
6407 const char *new_changelist;
6408 const apr_array_header_t *changelist_filter;
6409 svn_depth_t depth;
6410 };
6411
6412
6413 /* The main part of svn_wc__db_op_set_changelist().
6414 *
6415 * Implements svn_wc__db_txn_callback_t. */
6416 static svn_error_t *
set_changelist_txn(void * baton,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)6417 set_changelist_txn(void *baton,
6418 svn_wc__db_wcroot_t *wcroot,
6419 const char *local_relpath,
6420 apr_pool_t *scratch_pool)
6421 {
6422 struct set_changelist_baton_t *scb = baton;
6423 svn_sqlite__stmt_t *stmt;
6424
6425 SVN_ERR(populate_targets_tree(wcroot, local_relpath, scb->depth,
6426 scb->changelist_filter, scratch_pool));
6427
6428 /* Ensure we have actual nodes for our targets. */
6429 if (scb->new_changelist)
6430 {
6431 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6432 STMT_INSERT_ACTUAL_EMPTIES_FILES));
6433 SVN_ERR(svn_sqlite__step_done(stmt));
6434 }
6435
6436 /* Now create our notification table. */
6437 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6438 STMT_CREATE_CHANGELIST_LIST));
6439 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6440 STMT_CREATE_CHANGELIST_TRIGGER));
6441
6442 /* Update our changelists. */
6443 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6444 STMT_UPDATE_ACTUAL_CHANGELISTS));
6445 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6446 scb->new_changelist));
6447 SVN_ERR(svn_sqlite__step_done(stmt));
6448
6449 if (scb->new_changelist)
6450 {
6451 /* We have to notify that we skipped directories, so do that now. */
6452 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6453 STMT_MARK_SKIPPED_CHANGELIST_DIRS));
6454 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6455 scb->new_changelist));
6456 SVN_ERR(svn_sqlite__step_done(stmt));
6457 }
6458
6459 /* We may have left empty ACTUAL nodes, so remove them. This is only a
6460 potential problem if we removed changelists. */
6461 if (!scb->new_changelist)
6462 {
6463 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6464 STMT_DELETE_ACTUAL_EMPTIES));
6465 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6466 SVN_ERR(svn_sqlite__step_done(stmt));
6467 }
6468
6469 return SVN_NO_ERROR;
6470 }
6471
6472
6473 /* Send notifications for svn_wc__db_op_set_changelist().
6474 *
6475 * Implements work_callback_t. */
6476 static svn_error_t *
do_changelist_notify(void * baton,svn_wc__db_wcroot_t * wcroot,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)6477 do_changelist_notify(void *baton,
6478 svn_wc__db_wcroot_t *wcroot,
6479 svn_cancel_func_t cancel_func,
6480 void *cancel_baton,
6481 svn_wc_notify_func2_t notify_func,
6482 void *notify_baton,
6483 apr_pool_t *scratch_pool)
6484 {
6485 svn_sqlite__stmt_t *stmt;
6486 svn_boolean_t have_row;
6487 apr_pool_t *iterpool;
6488
6489 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6490 STMT_SELECT_CHANGELIST_LIST));
6491 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6492
6493 iterpool = svn_pool_create(scratch_pool);
6494 while (have_row)
6495 {
6496 /* ### wc_id is column 0. use it one day... */
6497 const char *notify_relpath = svn_sqlite__column_text(stmt, 1, NULL);
6498 svn_wc_notify_action_t action = svn_sqlite__column_int(stmt, 2);
6499 svn_wc_notify_t *notify;
6500 const char *notify_abspath;
6501
6502 svn_pool_clear(iterpool);
6503
6504 if (cancel_func)
6505 {
6506 svn_error_t *err = cancel_func(cancel_baton);
6507
6508 if (err)
6509 return svn_error_trace(svn_error_compose_create(
6510 err,
6511 svn_sqlite__reset(stmt)));
6512 }
6513
6514 notify_abspath = svn_dirent_join(wcroot->abspath, notify_relpath,
6515 iterpool);
6516 notify = svn_wc_create_notify(notify_abspath, action, iterpool);
6517 notify->changelist_name = svn_sqlite__column_text(stmt, 3, NULL);
6518 notify_func(notify_baton, notify, iterpool);
6519
6520 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6521 }
6522 svn_pool_destroy(iterpool);
6523
6524 return svn_error_trace(svn_sqlite__reset(stmt));
6525 }
6526
6527
6528 svn_error_t *
svn_wc__db_op_set_changelist(svn_wc__db_t * db,const char * local_abspath,const char * new_changelist,const apr_array_header_t * changelist_filter,svn_depth_t depth,svn_wc_notify_func2_t notify_func,void * notify_baton,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)6529 svn_wc__db_op_set_changelist(svn_wc__db_t *db,
6530 const char *local_abspath,
6531 const char *new_changelist,
6532 const apr_array_header_t *changelist_filter,
6533 svn_depth_t depth,
6534 svn_wc_notify_func2_t notify_func,
6535 void *notify_baton,
6536 svn_cancel_func_t cancel_func,
6537 void *cancel_baton,
6538 apr_pool_t *scratch_pool)
6539 {
6540 svn_wc__db_wcroot_t *wcroot;
6541 const char *local_relpath;
6542 struct set_changelist_baton_t scb;
6543
6544 scb.new_changelist = new_changelist;
6545 scb.changelist_filter = changelist_filter;
6546 scb.depth = depth;
6547
6548 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6549
6550 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6551 db, local_abspath,
6552 scratch_pool, scratch_pool));
6553 VERIFY_USABLE_WCROOT(wcroot);
6554
6555 /* Flush the entries before we do the work. Even if no work is performed,
6556 the flush isn't a problem. */
6557 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6558
6559 /* Perform the set-changelist operation (transactionally), perform any
6560 notifications necessary, and then clean out our temporary tables. */
6561 return svn_error_trace(with_finalization(wcroot, local_relpath,
6562 set_changelist_txn, &scb,
6563 do_changelist_notify, NULL,
6564 cancel_func, cancel_baton,
6565 notify_func, notify_baton,
6566 STMT_FINALIZE_CHANGELIST,
6567 scratch_pool));
6568 }
6569
6570 /* Implementation of svn_wc__db_op_mark_conflict() */
6571 svn_error_t *
svn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,const svn_skel_t * conflict_skel,apr_pool_t * scratch_pool)6572 svn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t *wcroot,
6573 const char *local_relpath,
6574 const svn_skel_t *conflict_skel,
6575 apr_pool_t *scratch_pool)
6576 {
6577 svn_sqlite__stmt_t *stmt;
6578 svn_boolean_t got_row;
6579 svn_boolean_t is_complete;
6580
6581 SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict_skel));
6582 SVN_ERR_ASSERT(is_complete);
6583
6584 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6585 STMT_SELECT_ACTUAL_NODE));
6586 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6587 SVN_ERR(svn_sqlite__step(&got_row, stmt));
6588 SVN_ERR(svn_sqlite__reset(stmt));
6589
6590 if (got_row)
6591 {
6592 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6593 STMT_UPDATE_ACTUAL_CONFLICT));
6594 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6595 }
6596 else
6597 {
6598 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6599 STMT_INSERT_ACTUAL_CONFLICT));
6600 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6601 if (*local_relpath != '\0')
6602 SVN_ERR(svn_sqlite__bind_text(stmt, 4,
6603 svn_relpath_dirname(local_relpath,
6604 scratch_pool)));
6605 }
6606
6607 {
6608 svn_stringbuf_t *sb = svn_skel__unparse(conflict_skel, scratch_pool);
6609
6610 SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6611 }
6612
6613 SVN_ERR(svn_sqlite__update(NULL, stmt));
6614
6615 return SVN_NO_ERROR;
6616 }
6617
6618 svn_error_t *
svn_wc__db_op_mark_conflict(svn_wc__db_t * db,const char * local_abspath,const svn_skel_t * conflict_skel,const svn_skel_t * work_items,apr_pool_t * scratch_pool)6619 svn_wc__db_op_mark_conflict(svn_wc__db_t *db,
6620 const char *local_abspath,
6621 const svn_skel_t *conflict_skel,
6622 const svn_skel_t *work_items,
6623 apr_pool_t *scratch_pool)
6624 {
6625 svn_wc__db_wcroot_t *wcroot;
6626 const char *local_relpath;
6627
6628 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6629
6630 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6631 local_abspath, scratch_pool, scratch_pool));
6632 VERIFY_USABLE_WCROOT(wcroot);
6633
6634 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6635 conflict_skel, scratch_pool));
6636
6637 /* ### Should be handled in the same transaction as setting the conflict */
6638 if (work_items)
6639 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6640
6641 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6642
6643 return SVN_NO_ERROR;
6644
6645 }
6646
6647 /* The body of svn_wc__db_op_mark_resolved().
6648 */
6649 svn_error_t *
svn_wc__db_op_mark_resolved_internal(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_wc__db_t * db,svn_boolean_t resolved_text,svn_boolean_t resolved_props,svn_boolean_t resolved_tree,const svn_skel_t * work_items,apr_pool_t * scratch_pool)6650 svn_wc__db_op_mark_resolved_internal(svn_wc__db_wcroot_t *wcroot,
6651 const char *local_relpath,
6652 svn_wc__db_t *db,
6653 svn_boolean_t resolved_text,
6654 svn_boolean_t resolved_props,
6655 svn_boolean_t resolved_tree,
6656 const svn_skel_t *work_items,
6657 apr_pool_t *scratch_pool)
6658 {
6659 svn_sqlite__stmt_t *stmt;
6660 svn_boolean_t have_row;
6661 int total_affected_rows = 0;
6662 svn_boolean_t resolved_all;
6663 apr_size_t conflict_len;
6664 const void *conflict_data;
6665 svn_skel_t *conflicts;
6666
6667 /* Check if we have a conflict in ACTUAL */
6668 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6669 STMT_SELECT_ACTUAL_NODE));
6670 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6671
6672 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6673
6674 if (! have_row)
6675 {
6676 SVN_ERR(svn_sqlite__reset(stmt));
6677
6678 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6679 STMT_SELECT_NODE_INFO));
6680
6681 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6682
6683 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6684 SVN_ERR(svn_sqlite__reset(stmt));
6685
6686 if (have_row)
6687 return SVN_NO_ERROR;
6688
6689 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6690 _("The node '%s' was not found."),
6691 path_for_error_message(wcroot,
6692 local_relpath,
6693 scratch_pool));
6694 }
6695
6696 conflict_data = svn_sqlite__column_blob(stmt, 2, &conflict_len,
6697 scratch_pool);
6698 SVN_ERR(svn_sqlite__reset(stmt));
6699 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6700
6701 if (!conflict_data)
6702 return SVN_NO_ERROR;
6703
6704 conflicts = svn_skel__parse(conflict_data, conflict_len, scratch_pool);
6705
6706
6707 SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
6708 db, wcroot->abspath,
6709 resolved_text,
6710 resolved_props ? "" : NULL,
6711 resolved_tree,
6712 scratch_pool, scratch_pool));
6713
6714 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6715 STMT_UPDATE_ACTUAL_CONFLICT));
6716 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6717
6718 if (! resolved_all)
6719 {
6720 svn_stringbuf_t *sb = svn_skel__unparse(conflicts, scratch_pool);
6721
6722 SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6723 }
6724
6725 SVN_ERR(svn_sqlite__update(&total_affected_rows, stmt));
6726
6727 /* Now, remove the actual node if it doesn't have any more useful
6728 information. We only need to do this if we've remove data ourselves. */
6729 if (total_affected_rows > 0)
6730 {
6731 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6732 STMT_DELETE_ACTUAL_EMPTY));
6733 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6734 SVN_ERR(svn_sqlite__step_done(stmt));
6735 }
6736
6737 return SVN_NO_ERROR;
6738 }
6739
6740 svn_error_t *
svn_wc__db_op_mark_resolved(svn_wc__db_t * db,const char * local_abspath,svn_boolean_t resolved_text,svn_boolean_t resolved_props,svn_boolean_t resolved_tree,const svn_skel_t * work_items,apr_pool_t * scratch_pool)6741 svn_wc__db_op_mark_resolved(svn_wc__db_t *db,
6742 const char *local_abspath,
6743 svn_boolean_t resolved_text,
6744 svn_boolean_t resolved_props,
6745 svn_boolean_t resolved_tree,
6746 const svn_skel_t *work_items,
6747 apr_pool_t *scratch_pool)
6748 {
6749 svn_wc__db_wcroot_t *wcroot;
6750 const char *local_relpath;
6751
6752 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6753
6754 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6755 local_abspath, scratch_pool, scratch_pool));
6756 VERIFY_USABLE_WCROOT(wcroot);
6757
6758 SVN_WC__DB_WITH_TXN(
6759 svn_wc__db_op_mark_resolved_internal(
6760 wcroot, local_relpath, db,
6761 resolved_text, resolved_props, resolved_tree,
6762 work_items, scratch_pool),
6763 wcroot);
6764
6765 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6766 return SVN_NO_ERROR;
6767 }
6768
6769 /* Clear moved-to information at the delete-half of the move which moved
6770 * MOVED_TO_RELPATH here. This transforms the delete part of the move into a
6771 * normal delete.
6772 *
6773 * Note that the moved-to location is always an op-root, while this is not the
6774 * case for a moved-from location.
6775 */
6776 static svn_error_t *
clear_moved_to(svn_wc__db_wcroot_t * wcroot,const char * moved_to_relpath,apr_pool_t * scratch_pool)6777 clear_moved_to(svn_wc__db_wcroot_t *wcroot,
6778 const char *moved_to_relpath,
6779 apr_pool_t *scratch_pool)
6780 {
6781 svn_sqlite__stmt_t *stmt;
6782 const char *moved_from_relpath;
6783 int moved_from_op_depth;
6784
6785 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6786 STMT_SELECT_MOVED_FROM_RELPATH));
6787 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, moved_to_relpath));
6788 SVN_ERR(svn_sqlite__step_row(stmt));
6789
6790 moved_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
6791 moved_from_op_depth = svn_sqlite__column_int(stmt, 1);
6792 SVN_ERR(svn_sqlite__reset(stmt));
6793
6794 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6795 STMT_CLEAR_MOVED_TO_RELPATH));
6796 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6797 moved_from_relpath, moved_from_op_depth));
6798 SVN_ERR(svn_sqlite__update(NULL, stmt));
6799
6800 return SVN_NO_ERROR;
6801 }
6802
6803 /* Helper function for op_revert_txn. Raises move tree conflicts on
6804 descendants to ensure database stability on a non recursive revert
6805 of an ancestor that contains a possible move related tree conflict.
6806 */
6807 static svn_error_t *
revert_maybe_raise_moved_away(svn_wc__db_wcroot_t * wcroot,svn_wc__db_t * db,const char * local_relpath,int op_depth_below,apr_pool_t * scratch_pool)6808 revert_maybe_raise_moved_away(svn_wc__db_wcroot_t * wcroot,
6809 svn_wc__db_t *db,
6810 const char *local_relpath,
6811 int op_depth_below,
6812 apr_pool_t *scratch_pool)
6813 {
6814 svn_skel_t *conflict;
6815 svn_wc_operation_t operation;
6816 svn_boolean_t tree_conflicted;
6817 const apr_array_header_t *locations;
6818 svn_wc_conflict_reason_t reason;
6819 svn_wc_conflict_action_t action;
6820
6821 SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, wcroot,
6822 local_relpath,
6823 scratch_pool, scratch_pool));
6824
6825 if (!conflict)
6826 return SVN_NO_ERROR;
6827
6828 SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, NULL, NULL,
6829 &tree_conflicted,
6830 db, wcroot->abspath,
6831 conflict,
6832 scratch_pool, scratch_pool));
6833
6834 if (!tree_conflicted
6835 || (operation != svn_wc_operation_update
6836 && operation != svn_wc_operation_switch))
6837 {
6838 return SVN_NO_ERROR;
6839 }
6840
6841 SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
6842 NULL, NULL,
6843 db, wcroot->abspath,
6844 conflict,
6845 scratch_pool,
6846 scratch_pool));
6847
6848 if (reason == svn_wc_conflict_reason_deleted
6849 || reason == svn_wc_conflict_reason_replaced)
6850 {
6851 SVN_ERR(svn_wc__db_op_raise_moved_away_internal(
6852 wcroot, local_relpath, op_depth_below, db,
6853 operation, action,
6854 (locations && locations->nelts > 0)
6855 ? APR_ARRAY_IDX(locations, 0,
6856 const svn_wc_conflict_version_t *)
6857 : NULL,
6858 (locations && locations->nelts > 1)
6859 ? APR_ARRAY_IDX(locations, 1,
6860 const svn_wc_conflict_version_t *)
6861 : NULL,
6862 scratch_pool));
6863
6864 /* Transform the move information into revert information */
6865 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6866 STMT_MOVE_NOTIFY_TO_REVERT));
6867 }
6868
6869 return SVN_NO_ERROR;
6870 }
6871
6872 /* Baton for op_revert_txn and op_revert_recursive_txn */
6873 struct revert_baton_t
6874 {
6875 svn_wc__db_t *db;
6876 svn_boolean_t clear_changelists;
6877 };
6878
6879 /* One of the two alternative bodies of svn_wc__db_op_revert().
6880 *
6881 * Implements svn_wc__db_txn_callback_t. */
6882 static svn_error_t *
op_revert_txn(void * baton,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)6883 op_revert_txn(void *baton,
6884 svn_wc__db_wcroot_t *wcroot,
6885 const char *local_relpath,
6886 apr_pool_t *scratch_pool)
6887 {
6888 struct revert_baton_t *rvb = baton;
6889 svn_wc__db_t *db = rvb->db;
6890 svn_sqlite__stmt_t *stmt;
6891 svn_boolean_t have_row;
6892 int op_depth;
6893 svn_boolean_t moved_here;
6894 int affected_rows;
6895 const char *moved_to;
6896 int op_depth_below;
6897
6898 /* ### Similar structure to op_revert_recursive_txn, should they be
6899 combined? */
6900
6901 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6902 STMT_SELECT_NODE_INFO));
6903 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6904 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6905 if (!have_row)
6906 {
6907 SVN_ERR(svn_sqlite__reset(stmt));
6908
6909 /* There was no NODE row, so attempt to delete an ACTUAL_NODE row. */
6910 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6911 STMT_DELETE_ACTUAL_NODE));
6912 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6913 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6914 if (affected_rows)
6915 {
6916 /* Can't do non-recursive actual-only revert if actual-only
6917 children exist. Raise an error to cancel the transaction. */
6918 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6919 STMT_ACTUAL_HAS_CHILDREN));
6920 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6921 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6922 SVN_ERR(svn_sqlite__reset(stmt));
6923 if (have_row)
6924 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6925 _("Can't revert '%s' without"
6926 " reverting children"),
6927 path_for_error_message(wcroot,
6928 local_relpath,
6929 scratch_pool));
6930 return SVN_NO_ERROR;
6931 }
6932
6933 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6934 _("The node '%s' was not found."),
6935 path_for_error_message(wcroot,
6936 local_relpath,
6937 scratch_pool));
6938 }
6939
6940 op_depth = svn_sqlite__column_int(stmt, 0);
6941 moved_here = svn_sqlite__column_boolean(stmt, 15);
6942 moved_to = svn_sqlite__column_text(stmt, 17, scratch_pool);
6943
6944 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6945 if (have_row)
6946 op_depth_below = svn_sqlite__column_int(stmt, 0);
6947 else
6948 op_depth_below = -1;
6949
6950 SVN_ERR(svn_sqlite__reset(stmt));
6951
6952 if (moved_to)
6953 {
6954 SVN_ERR(svn_wc__db_op_break_move_internal(wcroot,
6955 local_relpath, op_depth,
6956 moved_to, NULL, scratch_pool));
6957 }
6958
6959 if (op_depth > 0 && op_depth == relpath_depth(local_relpath))
6960 {
6961 int op_depth_increased;
6962
6963 /* Can't do non-recursive revert if children exist */
6964 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6965 STMT_SELECT_GE_OP_DEPTH_CHILDREN));
6966 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6967 local_relpath, op_depth));
6968 SVN_ERR(svn_sqlite__step(&have_row, stmt));
6969 SVN_ERR(svn_sqlite__reset(stmt));
6970 if (have_row)
6971 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6972 _("Can't revert '%s' without"
6973 " reverting children"),
6974 path_for_error_message(wcroot,
6975 local_relpath,
6976 scratch_pool));
6977
6978 /* Rewrite the op-depth of all deleted children making the
6979 direct children into roots of deletes. */
6980 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6981 STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE));
6982 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6983 local_relpath,
6984 op_depth));
6985 SVN_ERR(svn_sqlite__update(&op_depth_increased, stmt));
6986
6987 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6988 STMT_DELETE_WORKING_NODE));
6989 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6990 SVN_ERR(svn_sqlite__step_done(stmt));
6991
6992 /* ### This removes the lock, but what about the access baton? */
6993 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6994 STMT_DELETE_WC_LOCK_ORPHAN));
6995 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6996 SVN_ERR(svn_sqlite__step_done(stmt));
6997
6998 /* If this node was moved-here, clear moved-to at the move source. */
6999 if (moved_here)
7000 SVN_ERR(clear_moved_to(wcroot, local_relpath, scratch_pool));
7001
7002 /* If the node was moved itself, we don't have interesting moved
7003 children (and the move itself was already broken) */
7004 if (op_depth_increased && !moved_to)
7005 SVN_ERR(revert_maybe_raise_moved_away(wcroot, db, local_relpath,
7006 op_depth_below, scratch_pool));
7007 }
7008
7009 if (rvb->clear_changelists)
7010 {
7011 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7012 STMT_DELETE_ACTUAL_NODE));
7013 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7014 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7015 }
7016 else
7017 {
7018 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7019 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST));
7020 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7021 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7022 if (!affected_rows)
7023 {
7024 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7025 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST));
7026 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7027 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7028 }
7029 }
7030
7031 return SVN_NO_ERROR;
7032 }
7033
7034
7035 /* One of the two alternative bodies of svn_wc__db_op_revert().
7036 *
7037 * Implements svn_wc__db_txn_callback_t. */
7038 static svn_error_t *
op_revert_recursive_txn(void * baton,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)7039 op_revert_recursive_txn(void *baton,
7040 svn_wc__db_wcroot_t *wcroot,
7041 const char *local_relpath,
7042 apr_pool_t *scratch_pool)
7043 {
7044 struct revert_baton_t *rvb = baton;
7045 svn_sqlite__stmt_t *stmt;
7046 svn_boolean_t have_row;
7047 int op_depth;
7048 int select_op_depth;
7049 svn_boolean_t moved_here;
7050 int affected_rows;
7051 apr_pool_t *iterpool;
7052
7053 /* ### Similar structure to op_revert_txn, should they be
7054 combined? */
7055
7056 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7057 STMT_SELECT_NODE_INFO));
7058 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7059 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7060 if (!have_row)
7061 {
7062 SVN_ERR(svn_sqlite__reset(stmt));
7063
7064 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7065 STMT_DELETE_ACTUAL_NODE));
7066 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7067 local_relpath));
7068 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7069
7070 if (affected_rows)
7071 return SVN_NO_ERROR; /* actual-only revert */
7072
7073 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7074 _("The node '%s' was not found."),
7075 path_for_error_message(wcroot,
7076 local_relpath,
7077 scratch_pool));
7078 }
7079
7080 op_depth = svn_sqlite__column_int(stmt, 0);
7081 moved_here = svn_sqlite__column_boolean(stmt, 15);
7082 SVN_ERR(svn_sqlite__reset(stmt));
7083
7084 if (op_depth > 0 && op_depth != relpath_depth(local_relpath))
7085 return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
7086 _("Can't revert '%s' without"
7087 " reverting parent"),
7088 path_for_error_message(wcroot,
7089 local_relpath,
7090 scratch_pool));
7091
7092 /* Remove moved-here from move destinations outside the tree. */
7093 SVN_ERR(svn_sqlite__get_statement(
7094 &stmt, wcroot->sdb, STMT_SELECT_MOVED_OUTSIDE));
7095 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
7096 op_depth));
7097 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7098 while (have_row)
7099 {
7100 const char *src_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7101 const char *dst_relpath = svn_sqlite__column_text(stmt, 1, NULL);
7102 int move_op_depth = svn_sqlite__column_int(stmt, 2);
7103 svn_error_t *err;
7104
7105 err = svn_wc__db_op_break_move_internal(wcroot,
7106 src_relpath, move_op_depth,
7107 dst_relpath, NULL, scratch_pool);
7108 if (err)
7109 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
7110
7111 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7112 }
7113 SVN_ERR(svn_sqlite__reset(stmt));
7114
7115 /* Don't delete BASE nodes */
7116 select_op_depth = op_depth ? op_depth : 1;
7117
7118 /* Reverting any non wc-root node */
7119 SVN_ERR(svn_sqlite__get_statement(
7120 &stmt, wcroot->sdb,
7121 STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
7122 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
7123 local_relpath, select_op_depth));
7124 SVN_ERR(svn_sqlite__step_done(stmt));
7125
7126 if (rvb->clear_changelists)
7127 {
7128 SVN_ERR(svn_sqlite__get_statement(
7129 &stmt, wcroot->sdb,
7130 STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7131 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7132 SVN_ERR(svn_sqlite__step_done(stmt));
7133 }
7134 else
7135 {
7136 SVN_ERR(svn_sqlite__get_statement(
7137 &stmt, wcroot->sdb,
7138 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
7139 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7140 SVN_ERR(svn_sqlite__step_done(stmt));
7141
7142 SVN_ERR(svn_sqlite__get_statement(
7143 &stmt, wcroot->sdb,
7144 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
7145 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7146 SVN_ERR(svn_sqlite__step_done(stmt));
7147 }
7148
7149 /* ### This removes the locks, but what about the access batons? */
7150 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7151 STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
7152 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7153 local_relpath));
7154 SVN_ERR(svn_sqlite__step_done(stmt));
7155
7156 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7157 STMT_SELECT_MOVED_HERE_CHILDREN));
7158 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7159
7160 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7161
7162 iterpool = svn_pool_create(scratch_pool);
7163 while (have_row)
7164 {
7165 const char *moved_here_child_relpath;
7166 svn_error_t *err;
7167
7168 svn_pool_clear(iterpool);
7169
7170 moved_here_child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
7171 err = clear_moved_to(wcroot, moved_here_child_relpath, iterpool);
7172 if (err)
7173 return svn_error_trace(svn_error_compose_create(
7174 err,
7175 svn_sqlite__reset(stmt)));
7176
7177 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7178 }
7179 SVN_ERR(svn_sqlite__reset(stmt));
7180 svn_pool_destroy(iterpool);
7181
7182 /* Clear potential moved-to pointing at the target node itself. */
7183 if (op_depth > 0 && op_depth == relpath_depth(local_relpath)
7184 && moved_here)
7185 SVN_ERR(clear_moved_to(wcroot, local_relpath, scratch_pool));
7186
7187 return SVN_NO_ERROR;
7188 }
7189
7190 svn_error_t *
svn_wc__db_op_revert(svn_wc__db_t * db,const char * local_abspath,svn_depth_t depth,svn_boolean_t clear_changelists,apr_pool_t * result_pool,apr_pool_t * scratch_pool)7191 svn_wc__db_op_revert(svn_wc__db_t *db,
7192 const char *local_abspath,
7193 svn_depth_t depth,
7194 svn_boolean_t clear_changelists,
7195 apr_pool_t *result_pool,
7196 apr_pool_t *scratch_pool)
7197 {
7198 svn_wc__db_wcroot_t *wcroot;
7199 const char *local_relpath;
7200 struct revert_baton_t rvb;
7201 struct with_triggers_baton_t wtb = { STMT_CREATE_REVERT_LIST,
7202 STMT_DROP_REVERT_LIST_TRIGGERS,
7203 NULL, NULL};
7204
7205 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7206
7207 rvb.db = db;
7208 rvb.clear_changelists = clear_changelists;
7209 wtb.cb_baton = &rvb;
7210
7211 switch (depth)
7212 {
7213 case svn_depth_empty:
7214 wtb.cb_func = op_revert_txn;
7215 break;
7216 case svn_depth_infinity:
7217 wtb.cb_func = op_revert_recursive_txn;
7218 break;
7219 default:
7220 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
7221 _("Unsupported depth for revert of '%s'"),
7222 svn_dirent_local_style(local_abspath,
7223 scratch_pool));
7224 }
7225
7226 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7227 db, local_abspath, scratch_pool, scratch_pool));
7228 VERIFY_USABLE_WCROOT(wcroot);
7229
7230 SVN_WC__DB_WITH_TXN(with_triggers(&wtb, wcroot, local_relpath, scratch_pool),
7231 wcroot);
7232
7233 SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
7234
7235 return SVN_NO_ERROR;
7236 }
7237
7238 /* The body of svn_wc__db_revert_list_read().
7239 */
7240 static svn_error_t *
revert_list_read(svn_boolean_t * reverted,const apr_array_header_t ** marker_paths,svn_boolean_t * copied_here,svn_node_kind_t * kind,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_wc__db_t * db,apr_pool_t * result_pool,apr_pool_t * scratch_pool)7241 revert_list_read(svn_boolean_t *reverted,
7242 const apr_array_header_t **marker_paths,
7243 svn_boolean_t *copied_here,
7244 svn_node_kind_t *kind,
7245 svn_wc__db_wcroot_t *wcroot,
7246 const char *local_relpath,
7247 svn_wc__db_t *db,
7248 apr_pool_t *result_pool,
7249 apr_pool_t *scratch_pool)
7250 {
7251 svn_sqlite__stmt_t *stmt;
7252 svn_boolean_t have_row;
7253
7254 *reverted = FALSE;
7255 *marker_paths = NULL;
7256 *copied_here = FALSE;
7257 *kind = svn_node_unknown;
7258
7259 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7260 STMT_SELECT_REVERT_LIST));
7261 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7262 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7263 if (have_row)
7264 {
7265 svn_boolean_t is_actual = svn_sqlite__column_boolean(stmt, 0);
7266 svn_boolean_t another_row = FALSE;
7267
7268 if (is_actual)
7269 {
7270 apr_size_t conflict_len;
7271 const void *conflict_data;
7272
7273 conflict_data = svn_sqlite__column_blob(stmt, 5, &conflict_len,
7274 scratch_pool);
7275 if (conflict_data)
7276 {
7277 svn_skel_t *conflicts = svn_skel__parse(conflict_data,
7278 conflict_len,
7279 scratch_pool);
7280
7281 SVN_ERR(svn_wc__conflict_read_markers(marker_paths,
7282 db, wcroot->abspath,
7283 conflicts,
7284 result_pool,
7285 scratch_pool));
7286 }
7287
7288 if (!svn_sqlite__column_is_null(stmt, 1)) /* notify */
7289 *reverted = TRUE;
7290
7291 SVN_ERR(svn_sqlite__step(&another_row, stmt));
7292 }
7293
7294 if (!is_actual || another_row)
7295 {
7296 *reverted = TRUE;
7297 if (!svn_sqlite__column_is_null(stmt, 4)) /* repos_id */
7298 {
7299 int op_depth = svn_sqlite__column_int(stmt, 3);
7300 *copied_here = (op_depth == relpath_depth(local_relpath));
7301 }
7302 *kind = svn_sqlite__column_token(stmt, 2, kind_map);
7303 }
7304
7305 }
7306 SVN_ERR(svn_sqlite__reset(stmt));
7307
7308 if (have_row)
7309 {
7310 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7311 STMT_DELETE_REVERT_LIST));
7312 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7313 SVN_ERR(svn_sqlite__step_done(stmt));
7314 }
7315
7316 return SVN_NO_ERROR;
7317 }
7318
7319 svn_error_t *
svn_wc__db_revert_list_read(svn_boolean_t * reverted,const apr_array_header_t ** marker_files,svn_boolean_t * copied_here,svn_node_kind_t * kind,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)7320 svn_wc__db_revert_list_read(svn_boolean_t *reverted,
7321 const apr_array_header_t **marker_files,
7322 svn_boolean_t *copied_here,
7323 svn_node_kind_t *kind,
7324 svn_wc__db_t *db,
7325 const char *local_abspath,
7326 apr_pool_t *result_pool,
7327 apr_pool_t *scratch_pool)
7328 {
7329 svn_wc__db_wcroot_t *wcroot;
7330 const char *local_relpath;
7331
7332 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7333 db, local_abspath, scratch_pool, scratch_pool));
7334 VERIFY_USABLE_WCROOT(wcroot);
7335
7336 SVN_WC__DB_WITH_TXN(
7337 revert_list_read(reverted, marker_files, copied_here, kind,
7338 wcroot, local_relpath, db,
7339 result_pool, scratch_pool),
7340 wcroot);
7341 return SVN_NO_ERROR;
7342 }
7343
7344
7345 /* The body of svn_wc__db_revert_list_read_copied_children().
7346 */
7347 static svn_error_t *
revert_list_read_copied_children(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_array_header_t ** children_p,apr_pool_t * result_pool,apr_pool_t * scratch_pool)7348 revert_list_read_copied_children(svn_wc__db_wcroot_t *wcroot,
7349 const char *local_relpath,
7350 apr_array_header_t **children_p,
7351 apr_pool_t *result_pool,
7352 apr_pool_t *scratch_pool)
7353 {
7354 svn_sqlite__stmt_t *stmt;
7355 svn_boolean_t have_row;
7356 apr_array_header_t *children;
7357
7358 children =
7359 apr_array_make(result_pool, 0,
7360 sizeof(svn_wc__db_revert_list_copied_child_info_t *));
7361
7362 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7363 STMT_SELECT_REVERT_LIST_COPIED_CHILDREN));
7364 SVN_ERR(svn_sqlite__bindf(stmt, "sd",
7365 local_relpath, relpath_depth(local_relpath)));
7366 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7367 while (have_row)
7368 {
7369 svn_wc__db_revert_list_copied_child_info_t *child_info;
7370 const char *child_relpath;
7371
7372 child_info = apr_palloc(result_pool, sizeof(*child_info));
7373
7374 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7375 child_info->abspath = svn_dirent_join(wcroot->abspath, child_relpath,
7376 result_pool);
7377 child_info->kind = svn_sqlite__column_token(stmt, 1, kind_map);
7378 APR_ARRAY_PUSH(
7379 children,
7380 svn_wc__db_revert_list_copied_child_info_t *) = child_info;
7381
7382 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7383 }
7384 SVN_ERR(svn_sqlite__reset(stmt));
7385
7386 *children_p = children;
7387
7388 return SVN_NO_ERROR;
7389 }
7390
7391
7392 svn_error_t *
svn_wc__db_revert_list_read_copied_children(apr_array_header_t ** children,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)7393 svn_wc__db_revert_list_read_copied_children(apr_array_header_t **children,
7394 svn_wc__db_t *db,
7395 const char *local_abspath,
7396 apr_pool_t *result_pool,
7397 apr_pool_t *scratch_pool)
7398 {
7399 svn_wc__db_wcroot_t *wcroot;
7400 const char *local_relpath;
7401
7402 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7403 db, local_abspath, scratch_pool, scratch_pool));
7404 VERIFY_USABLE_WCROOT(wcroot);
7405
7406 SVN_WC__DB_WITH_TXN(
7407 revert_list_read_copied_children(wcroot, local_relpath, children,
7408 result_pool, scratch_pool),
7409 wcroot);
7410 return SVN_NO_ERROR;
7411 }
7412
7413
7414 svn_error_t *
svn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func,void * notify_baton,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)7415 svn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func,
7416 void *notify_baton,
7417 svn_wc__db_t *db,
7418 const char *local_abspath,
7419 apr_pool_t *scratch_pool)
7420 {
7421 svn_wc__db_wcroot_t *wcroot;
7422 const char *local_relpath;
7423 svn_sqlite__stmt_t *stmt;
7424 svn_boolean_t have_row;
7425 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
7426
7427 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7428 db, local_abspath, scratch_pool, iterpool));
7429 VERIFY_USABLE_WCROOT(wcroot);
7430
7431 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7432 STMT_SELECT_REVERT_LIST_RECURSIVE));
7433 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7434 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7435 if (!have_row)
7436 return svn_error_trace(svn_sqlite__reset(stmt)); /* optimise for no row */
7437 while (have_row)
7438 {
7439 const char *notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7440 svn_wc_notify_t *notify;
7441
7442 svn_pool_clear(iterpool);
7443
7444 notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
7445 notify_relpath,
7446 iterpool),
7447 svn_wc_notify_revert,
7448 iterpool);
7449
7450 if (!svn_sqlite__column_is_null(stmt, 1))
7451 notify->kind = svn_sqlite__column_token(stmt, 1, kind_map);
7452 else
7453 {
7454 if (!svn_sqlite__column_is_null(stmt, 3))
7455 notify->kind = svn_sqlite__column_token(stmt, 3, kind_map_none);
7456
7457 switch (svn_sqlite__column_int(stmt, 2))
7458 {
7459 case 0:
7460 continue;
7461 case 1:
7462 /* standard revert */
7463 break;
7464 case 2:
7465 notify->action = svn_wc_notify_tree_conflict;
7466 break;
7467 default:
7468 SVN_ERR_MALFUNCTION();
7469 }
7470 }
7471
7472 notify_func(notify_baton, notify, iterpool);
7473
7474 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7475 }
7476 SVN_ERR(svn_sqlite__reset(stmt));
7477
7478 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7479 STMT_DELETE_REVERT_LIST_RECURSIVE));
7480 SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7481 SVN_ERR(svn_sqlite__step_done(stmt));
7482
7483 svn_pool_destroy(iterpool);
7484
7485 return SVN_NO_ERROR;
7486 }
7487
7488 svn_error_t *
svn_wc__db_revert_list_done(svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)7489 svn_wc__db_revert_list_done(svn_wc__db_t *db,
7490 const char *local_abspath,
7491 apr_pool_t *scratch_pool)
7492 {
7493 svn_wc__db_wcroot_t *wcroot;
7494 const char *local_relpath;
7495
7496 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7497 db, local_abspath, scratch_pool, scratch_pool));
7498 VERIFY_USABLE_WCROOT(wcroot);
7499
7500 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_DROP_REVERT_LIST));
7501
7502 return SVN_NO_ERROR;
7503 }
7504
7505 /* The body of svn_wc__db_op_remove_node().
7506 */
7507 static svn_error_t *
remove_node_txn(svn_boolean_t * left_changes,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_wc__db_t * db,svn_boolean_t destroy_wc,svn_boolean_t destroy_changes,const svn_skel_t * conflict,const svn_skel_t * work_items,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)7508 remove_node_txn(svn_boolean_t *left_changes,
7509 svn_wc__db_wcroot_t *wcroot,
7510 const char *local_relpath,
7511 svn_wc__db_t *db,
7512 svn_boolean_t destroy_wc,
7513 svn_boolean_t destroy_changes,
7514 const svn_skel_t *conflict,
7515 const svn_skel_t *work_items,
7516 svn_cancel_func_t cancel_func,
7517 void *cancel_baton,
7518 apr_pool_t *scratch_pool)
7519 {
7520 svn_sqlite__stmt_t *stmt;
7521
7522 /* Note that unlike many similar functions it is a valid scenario for this
7523 function to be called on a wcroot! */
7524
7525 /* db set when destroying wc */
7526 SVN_ERR_ASSERT(!destroy_wc || db != NULL);
7527
7528 if (left_changes)
7529 *left_changes = FALSE;
7530
7531 if (destroy_wc
7532 && (!destroy_changes || *local_relpath == '\0'))
7533 {
7534 svn_boolean_t have_row;
7535 apr_pool_t *iterpool;
7536 svn_error_t *err = NULL;
7537
7538 /* Install WQ items for deleting the unmodified files and all dirs */
7539 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7540 STMT_SELECT_WORKING_PRESENT));
7541 SVN_ERR(svn_sqlite__bindf(stmt, "is",
7542 wcroot->wc_id, local_relpath));
7543
7544 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7545
7546 iterpool = svn_pool_create(scratch_pool);
7547
7548 while (have_row)
7549 {
7550 const char *child_relpath;
7551 const char *child_abspath;
7552 svn_node_kind_t child_kind;
7553 svn_boolean_t have_checksum;
7554 svn_filesize_t recorded_size;
7555 apr_int64_t recorded_time;
7556 const svn_io_dirent2_t *dirent;
7557 svn_boolean_t modified_p = TRUE;
7558 svn_skel_t *work_item = NULL;
7559
7560 svn_pool_clear(iterpool);
7561
7562 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7563 child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
7564
7565 child_abspath = svn_dirent_join(wcroot->abspath, child_relpath,
7566 iterpool);
7567
7568 if (child_kind == svn_node_file)
7569 {
7570 have_checksum = !svn_sqlite__column_is_null(stmt, 2);
7571 recorded_size = get_recorded_size(stmt, 3);
7572 recorded_time = svn_sqlite__column_int64(stmt, 4);
7573 }
7574
7575 if (cancel_func)
7576 err = cancel_func(cancel_baton);
7577
7578 if (err)
7579 break;
7580
7581 err = svn_io_stat_dirent2(&dirent, child_abspath, FALSE, TRUE,
7582 iterpool, iterpool);
7583
7584 if (err)
7585 break;
7586
7587 if (destroy_changes
7588 || dirent->kind != svn_node_file
7589 || child_kind != svn_node_file)
7590 {
7591 /* Not interested in keeping changes */
7592 modified_p = FALSE;
7593 }
7594 else if (child_kind == svn_node_file
7595 && dirent->kind == svn_node_file
7596 && dirent->filesize == recorded_size
7597 && dirent->mtime == recorded_time)
7598 {
7599 modified_p = FALSE; /* File matches recorded state */
7600 }
7601 else if (have_checksum)
7602 err = svn_wc__internal_file_modified_p(&modified_p,
7603 db, child_abspath,
7604 FALSE, iterpool);
7605
7606 if (err)
7607 break;
7608
7609 if (modified_p)
7610 {
7611 if (left_changes)
7612 *left_changes = TRUE;
7613 }
7614 else if (child_kind == svn_node_dir)
7615 {
7616 err = svn_wc__wq_build_dir_remove(&work_item,
7617 db, wcroot->abspath,
7618 child_abspath, FALSE,
7619 iterpool, iterpool);
7620 }
7621 else /* svn_node_file || svn_node_symlink */
7622 {
7623 err = svn_wc__wq_build_file_remove(&work_item,
7624 db, wcroot->abspath,
7625 child_abspath,
7626 iterpool, iterpool);
7627 }
7628
7629 if (err)
7630 break;
7631
7632 if (work_item)
7633 {
7634 err = add_work_items(wcroot->sdb, work_item, iterpool);
7635 if (err)
7636 break;
7637 }
7638
7639 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7640 }
7641 svn_pool_destroy(iterpool);
7642
7643 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
7644 }
7645
7646 if (destroy_wc && *local_relpath != '\0')
7647 {
7648 /* Create work item for destroying the root */
7649 svn_wc__db_status_t status;
7650 svn_node_kind_t kind;
7651 SVN_ERR(read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, NULL,
7652 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7653 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7654 wcroot, local_relpath,
7655 scratch_pool, scratch_pool));
7656
7657 if (status == svn_wc__db_status_normal
7658 || status == svn_wc__db_status_added
7659 || status == svn_wc__db_status_incomplete)
7660 {
7661 svn_skel_t *work_item = NULL;
7662 const char *local_abspath = svn_dirent_join(wcroot->abspath,
7663 local_relpath,
7664 scratch_pool);
7665
7666 if (kind == svn_node_dir)
7667 {
7668 SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
7669 db, wcroot->abspath,
7670 local_abspath,
7671 destroy_changes
7672 /* recursive */,
7673 scratch_pool, scratch_pool));
7674 }
7675 else
7676 {
7677 svn_boolean_t modified_p = FALSE;
7678
7679 if (!destroy_changes)
7680 {
7681 SVN_ERR(svn_wc__internal_file_modified_p(&modified_p,
7682 db, local_abspath,
7683 FALSE,
7684 scratch_pool));
7685 }
7686
7687 if (!modified_p)
7688 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
7689 db, wcroot->abspath,
7690 local_abspath,
7691 scratch_pool,
7692 scratch_pool));
7693 else
7694 {
7695 if (left_changes)
7696 *left_changes = TRUE;
7697 }
7698 }
7699
7700 SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
7701 }
7702 }
7703
7704 /* Remove all nodes below local_relpath */
7705 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7706 STMT_DELETE_NODE_RECURSIVE));
7707 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7708 SVN_ERR(svn_sqlite__step_done(stmt));
7709
7710 /* Delete the root NODE when this is not the working copy root */
7711 if (local_relpath[0] != '\0')
7712 {
7713 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7714 STMT_DELETE_NODE_ALL_LAYERS));
7715 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7716 SVN_ERR(svn_sqlite__step_done(stmt));
7717 }
7718
7719 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7720 STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7721
7722 /* Delete all actual nodes at or below local_relpath */
7723 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7724 local_relpath));
7725 SVN_ERR(svn_sqlite__step_done(stmt));
7726
7727 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
7728 if (conflict)
7729 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
7730 conflict, scratch_pool));
7731
7732 return SVN_NO_ERROR;
7733 }
7734
7735 svn_error_t *
svn_wc__db_op_remove_node(svn_boolean_t * left_changes,svn_wc__db_t * db,const char * local_abspath,svn_boolean_t destroy_wc,svn_boolean_t destroy_changes,const svn_skel_t * conflict,const svn_skel_t * work_items,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)7736 svn_wc__db_op_remove_node(svn_boolean_t *left_changes,
7737 svn_wc__db_t *db,
7738 const char *local_abspath,
7739 svn_boolean_t destroy_wc,
7740 svn_boolean_t destroy_changes,
7741 const svn_skel_t *conflict,
7742 const svn_skel_t *work_items,
7743 svn_cancel_func_t cancel_func,
7744 void *cancel_baton,
7745 apr_pool_t *scratch_pool)
7746 {
7747 svn_wc__db_wcroot_t *wcroot;
7748 const char *local_relpath;
7749
7750 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7751
7752 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7753 local_abspath, scratch_pool, scratch_pool));
7754 VERIFY_USABLE_WCROOT(wcroot);
7755
7756 SVN_WC__DB_WITH_TXN(remove_node_txn(left_changes,
7757 wcroot, local_relpath, db,
7758 destroy_wc, destroy_changes,
7759 conflict, work_items,
7760 cancel_func, cancel_baton, scratch_pool),
7761 wcroot);
7762
7763 /* Flush everything below this node in all ways */
7764 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
7765 scratch_pool));
7766
7767 return SVN_NO_ERROR;
7768 }
7769
7770
7771 /* The body of svn_wc__db_op_set_base_depth().
7772 */
7773 static svn_error_t *
db_op_set_base_depth(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_depth_t depth,apr_pool_t * scratch_pool)7774 db_op_set_base_depth(svn_wc__db_wcroot_t *wcroot,
7775 const char *local_relpath,
7776 svn_depth_t depth,
7777 apr_pool_t *scratch_pool)
7778 {
7779 svn_sqlite__stmt_t *stmt;
7780 int affected_rows;
7781
7782 /* Flush any entries before we start monkeying the database. */
7783 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7784 STMT_UPDATE_NODE_BASE_DEPTH));
7785 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
7786 svn_token__to_word(depth_map, depth)));
7787 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7788
7789 if (affected_rows == 0)
7790 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7791 _("The node '%s' is not a committed directory"),
7792 path_for_error_message(wcroot, local_relpath,
7793 scratch_pool));
7794
7795 return SVN_NO_ERROR;
7796 }
7797
7798
7799 svn_error_t *
svn_wc__db_op_set_base_depth(svn_wc__db_t * db,const char * local_abspath,svn_depth_t depth,apr_pool_t * scratch_pool)7800 svn_wc__db_op_set_base_depth(svn_wc__db_t *db,
7801 const char *local_abspath,
7802 svn_depth_t depth,
7803 apr_pool_t *scratch_pool)
7804 {
7805 svn_wc__db_wcroot_t *wcroot;
7806 const char *local_relpath;
7807
7808 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7809 SVN_ERR_ASSERT(depth >= svn_depth_empty && depth <= svn_depth_infinity);
7810
7811 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7812 local_abspath, scratch_pool, scratch_pool));
7813 VERIFY_USABLE_WCROOT(wcroot);
7814
7815 /* ### We set depth on working and base to match entry behavior.
7816 Maybe these should be separated later? */
7817 SVN_WC__DB_WITH_TXN(db_op_set_base_depth(wcroot, local_relpath, depth,
7818 scratch_pool),
7819 wcroot);
7820
7821 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
7822
7823 return SVN_NO_ERROR;
7824 }
7825
7826
7827 static svn_error_t *
7828 info_below_working(svn_boolean_t *have_base,
7829 svn_boolean_t *have_work,
7830 svn_wc__db_status_t *status,
7831 svn_wc__db_wcroot_t *wcroot,
7832 const char *local_relpath,
7833 int below_op_depth, /* < 0 is ignored */
7834 apr_pool_t *scratch_pool);
7835
7836
7837 /* Convert STATUS, the raw status obtained from the presence map, to
7838 the status appropriate for a working (op_depth > 0) node and return
7839 it in *WORKING_STATUS. */
7840 static svn_error_t *
convert_to_working_status(svn_wc__db_status_t * working_status,svn_wc__db_status_t status)7841 convert_to_working_status(svn_wc__db_status_t *working_status,
7842 svn_wc__db_status_t status)
7843 {
7844 svn_wc__db_status_t work_status = status;
7845
7846 SVN_ERR_ASSERT(work_status == svn_wc__db_status_normal
7847 || work_status == svn_wc__db_status_not_present
7848 || work_status == svn_wc__db_status_base_deleted
7849 || work_status == svn_wc__db_status_incomplete
7850 || work_status == svn_wc__db_status_excluded);
7851
7852 if (work_status == svn_wc__db_status_excluded)
7853 {
7854 *working_status = svn_wc__db_status_excluded;
7855 }
7856 else if (work_status == svn_wc__db_status_not_present
7857 || work_status == svn_wc__db_status_base_deleted)
7858 {
7859 /* The caller should scan upwards to detect whether this
7860 deletion has occurred because this node has been moved
7861 away, or it is a regular deletion. Also note that the
7862 deletion could be of the BASE tree, or a child of
7863 something that has been copied/moved here. */
7864
7865 *working_status = svn_wc__db_status_deleted;
7866 }
7867 else /* normal or incomplete */
7868 {
7869 /* The caller should scan upwards to detect whether this
7870 addition has occurred because of a simple addition,
7871 a copy, or is the destination of a move. */
7872 *working_status = svn_wc__db_status_added;
7873 }
7874
7875 return SVN_NO_ERROR;
7876 }
7877
7878
7879 /* Return the status of the node, if any, below the "working" node (or
7880 below BELOW_OP_DEPTH if >= 0).
7881 Set *HAVE_BASE or *HAVE_WORK to indicate if a base node or lower
7882 working node is present, and *STATUS to the status of the first
7883 layer below the selected node. */
7884 static svn_error_t *
info_below_working(svn_boolean_t * have_base,svn_boolean_t * have_work,svn_wc__db_status_t * status,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int below_op_depth,apr_pool_t * scratch_pool)7885 info_below_working(svn_boolean_t *have_base,
7886 svn_boolean_t *have_work,
7887 svn_wc__db_status_t *status,
7888 svn_wc__db_wcroot_t *wcroot,
7889 const char *local_relpath,
7890 int below_op_depth,
7891 apr_pool_t *scratch_pool)
7892 {
7893 svn_sqlite__stmt_t *stmt;
7894 svn_boolean_t have_row;
7895
7896 *have_base = *have_work = FALSE;
7897 *status = svn_wc__db_status_normal;
7898
7899 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7900 STMT_SELECT_NODE_INFO));
7901 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7902 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7903
7904 if (below_op_depth >= 0)
7905 {
7906 while (have_row &&
7907 (svn_sqlite__column_int(stmt, 0) > below_op_depth))
7908 {
7909 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7910 }
7911 }
7912 if (have_row)
7913 {
7914 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7915 if (have_row)
7916 *status = svn_sqlite__column_token(stmt, 3, presence_map);
7917
7918 while (have_row)
7919 {
7920 int op_depth = svn_sqlite__column_int(stmt, 0);
7921
7922 if (op_depth > 0)
7923 *have_work = TRUE;
7924 else
7925 *have_base = TRUE;
7926
7927 SVN_ERR(svn_sqlite__step(&have_row, stmt));
7928 }
7929 }
7930 SVN_ERR(svn_sqlite__reset(stmt));
7931
7932 if (*have_work)
7933 SVN_ERR(convert_to_working_status(status, *status));
7934
7935 return SVN_NO_ERROR;
7936 }
7937
7938 /* Helper function for op_delete_txn */
7939 static svn_error_t *
delete_update_movedto(svn_wc__db_wcroot_t * wcroot,const char * child_moved_from_relpath,int op_depth,const char * new_moved_to_relpath,apr_pool_t * scratch_pool)7940 delete_update_movedto(svn_wc__db_wcroot_t *wcroot,
7941 const char *child_moved_from_relpath,
7942 int op_depth,
7943 const char *new_moved_to_relpath,
7944 apr_pool_t *scratch_pool)
7945 {
7946 svn_sqlite__stmt_t *stmt;
7947 int affected;
7948
7949 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7950 STMT_UPDATE_MOVED_TO_RELPATH));
7951
7952 SVN_ERR(svn_sqlite__bindf(stmt, "isds",
7953 wcroot->wc_id,
7954 child_moved_from_relpath,
7955 op_depth,
7956 new_moved_to_relpath));
7957 SVN_ERR(svn_sqlite__update(&affected, stmt));
7958 #ifdef SVN_DEBUG
7959 /* Not fatal in release mode. The move recording is broken,
7960 but the rest of the working copy can handle this. */
7961 SVN_ERR_ASSERT(affected == 1);
7962 #endif
7963
7964 return SVN_NO_ERROR;
7965 }
7966
7967
7968 struct op_delete_baton_t {
7969 const char *moved_to_relpath; /* NULL if delete is not part of a move */
7970 svn_skel_t *conflict;
7971 svn_skel_t *work_items;
7972 svn_boolean_t delete_dir_externals;
7973 svn_boolean_t notify;
7974 };
7975
7976 /* This structure is used while rewriting move information for nodes.
7977 *
7978 * The most simple case of rewriting move information happens when
7979 * a moved-away subtree is moved again: mv A B; mv B C
7980 * The second move requires rewriting moved-to info at or within A.
7981 *
7982 * Another example is a move of a subtree which had nodes moved into it:
7983 * mv A B/F; mv B G
7984 * This requires rewriting such that A/F is marked has having moved to G/F.
7985 *
7986 * Another case is where a node becomes a nested moved node.
7987 * A nested move happens when a subtree child is moved before or after
7988 * the subtree itself is moved. For example:
7989 * mv A/F A/G; mv A B
7990 * In this case, the move A/F -> A/G is rewritten to B/F -> B/G.
7991 * Note that the following sequence results in the same DB state:
7992 * mv A B; mv B/F B/G
7993 * We do not care about the order the moves were performed in.
7994 * For details, see https://cwiki.apache.org/confluence/display/SVN/MultiLayerMoves
7995 */
7996 struct moved_node_t {
7997 /* The source of the move. */
7998 const char *local_relpath;
7999
8000 /* The move destination. */
8001 const char *moved_to_relpath;
8002
8003 /* The op-depth of the deleted node at the source of the move. */
8004 int op_depth;
8005
8006 /* When >= 1 the op_depth at which local_relpath was moved to its
8007 location. Used to find its original location outside the delete */
8008 int moved_from_depth;
8009 };
8010
8011 /* Helper function to resolve the original location of local_relpath at OP_DEPTH
8012 before it was moved into the tree rooted at ROOT_RELPATH. */
8013 static svn_error_t *
resolve_moved_from(const char ** moved_from_relpath,int * moved_from_op_depth,svn_wc__db_wcroot_t * wcroot,const char * root_relpath,const char * local_relpath,int op_depth,apr_pool_t * result_pool,apr_pool_t * scratch_pool)8014 resolve_moved_from(const char **moved_from_relpath,
8015 int *moved_from_op_depth,
8016 svn_wc__db_wcroot_t *wcroot,
8017 const char *root_relpath,
8018 const char *local_relpath,
8019 int op_depth,
8020 apr_pool_t *result_pool,
8021 apr_pool_t *scratch_pool)
8022 {
8023 const char *suffix = "";
8024 svn_sqlite__stmt_t *stmt;
8025 const char *m_from_relpath;
8026 int m_from_op_depth;
8027 int m_move_from_depth;
8028 svn_boolean_t have_row;
8029
8030 while (relpath_depth(local_relpath) > op_depth)
8031 {
8032 const char *name;
8033 svn_relpath_split(&local_relpath, &name, local_relpath, scratch_pool);
8034 suffix = svn_relpath_join(suffix, name, scratch_pool);
8035 }
8036
8037 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8038 STMT_SELECT_MOVED_FROM_FOR_DELETE));
8039 SVN_ERR(svn_sqlite__bindf(stmt, "is",
8040 wcroot->wc_id, local_relpath));
8041 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8042
8043 if (!have_row)
8044 {
8045 /* assert(have_row); */
8046 *moved_from_relpath = NULL;
8047 *moved_from_op_depth = -1;
8048
8049 SVN_ERR(svn_sqlite__reset(stmt));
8050
8051 return SVN_NO_ERROR;
8052 }
8053
8054 m_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
8055 m_from_op_depth = svn_sqlite__column_int(stmt, 1);
8056 m_move_from_depth = svn_sqlite__column_int(stmt, 2);
8057
8058 SVN_ERR(svn_sqlite__reset(stmt));
8059
8060 if (! svn_relpath_skip_ancestor(root_relpath, m_from_relpath))
8061 {
8062 *moved_from_relpath = svn_relpath_join(m_from_relpath, suffix,
8063 result_pool);
8064 *moved_from_op_depth = m_from_op_depth; /* ### Ok? */
8065 return SVN_NO_ERROR;
8066 }
8067 else if (!m_move_from_depth)
8068 {
8069 *moved_from_relpath = NULL;
8070 *moved_from_op_depth = -1;
8071 return SVN_NO_ERROR;
8072 }
8073
8074 return svn_error_trace(
8075 resolve_moved_from(moved_from_relpath,
8076 moved_from_op_depth,
8077 wcroot,
8078 root_relpath,
8079 svn_relpath_join(m_from_relpath, suffix,
8080 scratch_pool),
8081 m_move_from_depth,
8082 result_pool, scratch_pool));
8083 }
8084
8085 static svn_error_t *
delete_node(void * baton,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)8086 delete_node(void *baton,
8087 svn_wc__db_wcroot_t *wcroot,
8088 const char *local_relpath,
8089 apr_pool_t *scratch_pool)
8090 {
8091 struct op_delete_baton_t *b = baton;
8092 svn_wc__db_status_t status;
8093 svn_boolean_t have_row, op_root;
8094 svn_boolean_t add_work = FALSE;
8095 svn_sqlite__stmt_t *stmt;
8096 int working_op_depth; /* Depth of what is to be deleted */
8097 int keep_op_depth = 0; /* Depth of what is below what is deleted */
8098 svn_node_kind_t kind;
8099 apr_array_header_t *moved_nodes = NULL;
8100 int delete_op_depth = relpath_depth(local_relpath);
8101
8102 assert(*local_relpath); /* Can't delete wcroot */
8103
8104 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8105 STMT_SELECT_NODE_INFO));
8106 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8107 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8108
8109 if (!have_row)
8110 {
8111 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
8112 svn_sqlite__reset(stmt),
8113 _("The node '%s' was not found."),
8114 path_for_error_message(wcroot,
8115 local_relpath,
8116 scratch_pool));
8117 }
8118
8119 working_op_depth = svn_sqlite__column_int(stmt, 0);
8120 status = svn_sqlite__column_token(stmt, 3, presence_map);
8121 kind = svn_sqlite__column_token(stmt, 4, kind_map);
8122
8123 if (working_op_depth < delete_op_depth)
8124 {
8125 op_root = FALSE;
8126 add_work = TRUE;
8127 keep_op_depth = working_op_depth;
8128 }
8129 else
8130 {
8131 op_root = TRUE;
8132
8133 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8134
8135 if (have_row)
8136 {
8137 svn_wc__db_status_t below_status;
8138 int below_op_depth;
8139
8140 below_op_depth = svn_sqlite__column_int(stmt, 0);
8141 below_status = svn_sqlite__column_token(stmt, 3, presence_map);
8142
8143 if (below_status != svn_wc__db_status_not_present
8144 && below_status != svn_wc__db_status_base_deleted)
8145 {
8146 add_work = TRUE;
8147 keep_op_depth = below_op_depth;
8148 }
8149 else
8150 keep_op_depth = 0;
8151 }
8152 else
8153 keep_op_depth = -1;
8154 }
8155
8156 SVN_ERR(svn_sqlite__reset(stmt));
8157
8158 if (working_op_depth != 0) /* WORKING */
8159 SVN_ERR(convert_to_working_status(&status, status));
8160
8161 if (status == svn_wc__db_status_deleted
8162 || status == svn_wc__db_status_not_present)
8163 return SVN_NO_ERROR;
8164
8165 /* Don't copy BASE directories with server excluded nodes */
8166 if (status == svn_wc__db_status_normal && kind == svn_node_dir)
8167 {
8168 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8169 STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
8170 SVN_ERR(svn_sqlite__bindf(stmt, "is",
8171 wcroot->wc_id, local_relpath));
8172 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8173 if (have_row)
8174 {
8175 const char *absent_path = svn_sqlite__column_text(stmt, 0,
8176 scratch_pool);
8177
8178 return svn_error_createf(
8179 SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
8180 svn_sqlite__reset(stmt),
8181 _("Cannot delete '%s' as '%s' is excluded by server"),
8182 path_for_error_message(wcroot, local_relpath,
8183 scratch_pool),
8184 path_for_error_message(wcroot, absent_path,
8185 scratch_pool));
8186 }
8187 SVN_ERR(svn_sqlite__reset(stmt));
8188 }
8189 else if (status == svn_wc__db_status_server_excluded)
8190 {
8191 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
8192 _("Cannot delete '%s' as it is excluded by server"),
8193 path_for_error_message(wcroot, local_relpath,
8194 scratch_pool));
8195 }
8196 else if (status == svn_wc__db_status_excluded)
8197 {
8198 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
8199 _("Cannot delete '%s' as it is excluded"),
8200 path_for_error_message(wcroot, local_relpath,
8201 scratch_pool));
8202 }
8203
8204 if (b->moved_to_relpath)
8205 {
8206 const char *moved_from_relpath = NULL;
8207 struct moved_node_t *moved_node;
8208 int move_op_depth;
8209
8210 moved_nodes = apr_array_make(scratch_pool, 1,
8211 sizeof(struct moved_node_t *));
8212
8213 /* The node is being moved-away.
8214 * Figure out if the node was moved-here before, or whether this
8215 * is the first time the node is moved. */
8216 if (status == svn_wc__db_status_added)
8217 SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL,
8218 &moved_from_relpath,
8219 NULL,
8220 &move_op_depth,
8221 wcroot, local_relpath,
8222 scratch_pool, scratch_pool));
8223
8224 if (op_root && moved_from_relpath)
8225 {
8226 const char *part = svn_relpath_skip_ancestor(local_relpath,
8227 moved_from_relpath);
8228
8229 /* Existing move-root is moved to another location */
8230 moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
8231 if (!part)
8232 moved_node->local_relpath = moved_from_relpath;
8233 else
8234 moved_node->local_relpath = svn_relpath_join(b->moved_to_relpath,
8235 part, scratch_pool);
8236 moved_node->op_depth = move_op_depth;
8237 moved_node->moved_to_relpath = b->moved_to_relpath;
8238 moved_node->moved_from_depth = -1;
8239
8240 APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
8241 }
8242 else if (!op_root && (status == svn_wc__db_status_normal
8243 || status == svn_wc__db_status_copied
8244 || status == svn_wc__db_status_moved_here))
8245 {
8246 /* The node is becoming a move-root for the first time,
8247 * possibly because of a nested move operation. */
8248 moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
8249 moved_node->local_relpath = local_relpath;
8250 moved_node->op_depth = delete_op_depth;
8251 moved_node->moved_to_relpath = b->moved_to_relpath;
8252 moved_node->moved_from_depth = -1;
8253
8254 APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
8255 }
8256 /* Else: We can't track history of local additions and/or of things we are
8257 about to delete. */
8258
8259 /* And update all moved_to values still pointing to this location */
8260 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8261 STMT_UPDATE_MOVED_TO_DESCENDANTS));
8262 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
8263 local_relpath,
8264 b->moved_to_relpath));
8265 SVN_ERR(svn_sqlite__update(NULL, stmt));
8266 }
8267
8268 /* Find children that were moved out of the subtree rooted at this node.
8269 * We'll need to update their op-depth columns because their deletion
8270 * is now implied by the deletion of their parent (i.e. this node). */
8271 {
8272 apr_pool_t *iterpool;
8273 int i;
8274
8275 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8276 STMT_SELECT_MOVED_FOR_DELETE));
8277 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
8278 delete_op_depth));
8279
8280 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8281 iterpool = svn_pool_create(scratch_pool);
8282 while (have_row)
8283 {
8284 struct moved_node_t *mn;
8285 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8286 const char *mv_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
8287 int child_op_depth = svn_sqlite__column_int(stmt, 2);
8288 int moved_from_depth = -1;
8289 svn_boolean_t fixup = FALSE;
8290
8291 if (! b->moved_to_relpath
8292 && ! svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
8293 {
8294 /* a NULL moved_here_depth will be reported as 0 */
8295 int moved_here_depth = svn_sqlite__column_int(stmt, 3);
8296
8297 /* Plain delete. Fixup move information of descendants that were
8298 moved here, or that were moved out */
8299
8300 if (moved_here_depth >= delete_op_depth)
8301 {
8302 /* The move we recorded here must be moved to the location
8303 this node had before it was moved here.
8304
8305 This might contain multiple steps when the node was moved
8306 in several places within the to be deleted tree */
8307
8308 /* ### TODO: Add logic */
8309 fixup = TRUE;
8310 moved_from_depth = moved_here_depth;
8311 }
8312 else
8313 {
8314 /* Update the op-depth of an moved away node that was
8315 registered as moved by the records that we are about
8316 to delete */
8317 fixup = TRUE;
8318 child_op_depth = delete_op_depth;
8319 }
8320 }
8321 else if (b->moved_to_relpath)
8322 {
8323 /* The node is moved to a new location */
8324
8325 if (delete_op_depth == child_op_depth)
8326 {
8327 /* Update the op-depth of a tree shadowed by this tree */
8328 fixup = TRUE;
8329 /*child_op_depth = delete_depth;*/
8330 }
8331 else if (child_op_depth >= delete_op_depth
8332 && !svn_relpath_skip_ancestor(local_relpath,
8333 mv_to_relpath))
8334 {
8335 /* Update the move destination of something that is now moved
8336 away further */
8337
8338 child_relpath = svn_relpath_skip_ancestor(local_relpath,
8339 child_relpath);
8340
8341 if (child_relpath)
8342 {
8343 child_relpath = svn_relpath_join(b->moved_to_relpath,
8344 child_relpath,
8345 scratch_pool);
8346
8347 if (child_op_depth > delete_op_depth
8348 && svn_relpath_skip_ancestor(local_relpath,
8349 child_relpath))
8350 child_op_depth = delete_op_depth;
8351 else
8352 {
8353 /* Calculate depth of the shadowing at the new location */
8354 child_op_depth = child_op_depth
8355 - relpath_depth(local_relpath)
8356 + relpath_depth(b->moved_to_relpath);
8357 }
8358
8359 fixup = TRUE;
8360 }
8361 }
8362 }
8363
8364 if (fixup)
8365 {
8366 mn = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
8367
8368 mn->local_relpath = apr_pstrdup(scratch_pool, child_relpath);
8369 mn->moved_to_relpath = apr_pstrdup(scratch_pool, mv_to_relpath);
8370 mn->op_depth = child_op_depth;
8371 mn->moved_from_depth = moved_from_depth;
8372
8373 if (!moved_nodes)
8374 moved_nodes = apr_array_make(scratch_pool, 1,
8375 sizeof(struct moved_node_t *));
8376 APR_ARRAY_PUSH(moved_nodes, struct moved_node_t *) = mn;
8377 }
8378
8379 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8380 }
8381 SVN_ERR(svn_sqlite__reset(stmt));
8382
8383 for (i = 0; moved_nodes && (i < moved_nodes->nelts); i++)
8384 {
8385 struct moved_node_t *mn = APR_ARRAY_IDX(moved_nodes, i,
8386 struct moved_node_t *);
8387
8388 if (mn->moved_from_depth > 0)
8389 {
8390 svn_pool_clear(iterpool);
8391
8392 SVN_ERR(resolve_moved_from(&mn->local_relpath, &mn->op_depth,
8393 wcroot, local_relpath,
8394 mn->local_relpath,
8395 mn->moved_from_depth,
8396 scratch_pool, iterpool));
8397
8398 if (!mn->local_relpath)
8399 SVN_ERR(svn_sort__array_delete2(moved_nodes, i--, 1));
8400 }
8401 }
8402
8403 svn_pool_destroy(iterpool);
8404 }
8405
8406 if (!b->moved_to_relpath)
8407 {
8408 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8409 STMT_CLEAR_MOVED_TO_DESCENDANTS));
8410 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8411 local_relpath));
8412 SVN_ERR(svn_sqlite__update(NULL, stmt));
8413
8414 if (op_root)
8415 {
8416 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8417 STMT_CLEAR_MOVED_TO_FROM_DEST));
8418 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8419 local_relpath));
8420
8421 SVN_ERR(svn_sqlite__update(NULL, stmt));
8422 }
8423 }
8424
8425
8426 /* ### Put actual-only nodes into the list? */
8427 if (b->notify)
8428 {
8429 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8430 STMT_INSERT_DELETE_LIST));
8431 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
8432 wcroot->wc_id, local_relpath, working_op_depth));
8433 SVN_ERR(svn_sqlite__step_done(stmt));
8434 }
8435
8436 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8437 STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
8438 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
8439 wcroot->wc_id, local_relpath, delete_op_depth));
8440 SVN_ERR(svn_sqlite__step_done(stmt));
8441
8442 /* Delete ACTUAL_NODE rows, but leave those that have changelist
8443 and a NODES row. */
8444 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8445 STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8446 SVN_ERR(svn_sqlite__bindf(stmt, "is",
8447 wcroot->wc_id, local_relpath));
8448 SVN_ERR(svn_sqlite__step_done(stmt));
8449
8450 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8451 STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8452 SVN_ERR(svn_sqlite__bindf(stmt, "is",
8453 wcroot->wc_id, local_relpath));
8454 SVN_ERR(svn_sqlite__step_done(stmt));
8455
8456 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8457 STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
8458 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8459 local_relpath));
8460 SVN_ERR(svn_sqlite__step_done(stmt));
8461
8462 if (add_work)
8463 {
8464 /* Delete the node at LOCAL_RELPATH, and possibly mark it as moved. */
8465
8466 /* Delete the node and possible descendants. */
8467 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8468 STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
8469 SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
8470 wcroot->wc_id, local_relpath,
8471 keep_op_depth, delete_op_depth));
8472 SVN_ERR(svn_sqlite__step_done(stmt));
8473 }
8474
8475 if (moved_nodes)
8476 {
8477 int i;
8478
8479 for (i = 0; i < moved_nodes->nelts; ++i)
8480 {
8481 const struct moved_node_t *moved_node
8482 = APR_ARRAY_IDX(moved_nodes, i, void *);
8483
8484 SVN_ERR(delete_update_movedto(wcroot,
8485 moved_node->local_relpath,
8486 moved_node->op_depth,
8487 moved_node->moved_to_relpath,
8488 scratch_pool));
8489 }
8490 }
8491
8492 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8493 STMT_DELETE_FILE_EXTERNALS));
8494 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8495 SVN_ERR(svn_sqlite__step_done(stmt));
8496
8497 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8498 b->delete_dir_externals
8499 ? STMT_DELETE_EXTERNAL_REGISTATIONS
8500 : STMT_DELETE_FILE_EXTERNAL_REGISTATIONS));
8501 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8502 SVN_ERR(svn_sqlite__step_done(stmt));
8503
8504 SVN_ERR(add_work_items(wcroot->sdb, b->work_items, scratch_pool));
8505 if (b->conflict)
8506 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
8507 b->conflict, scratch_pool));
8508
8509 return SVN_NO_ERROR;
8510 }
8511
8512 static svn_error_t *
op_delete_txn(void * baton,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)8513 op_delete_txn(void *baton,
8514 svn_wc__db_wcroot_t *wcroot,
8515 const char *local_relpath,
8516 apr_pool_t *scratch_pool)
8517 {
8518
8519 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8520 SVN_ERR(delete_node(baton, wcroot, local_relpath, scratch_pool));
8521 return SVN_NO_ERROR;
8522 }
8523
8524
8525 struct op_delete_many_baton_t {
8526 apr_array_header_t *rel_targets;
8527 svn_boolean_t delete_dir_externals;
8528 const svn_skel_t *work_items;
8529 };
8530
8531 static svn_error_t *
op_delete_many_txn(void * baton,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)8532 op_delete_many_txn(void *baton,
8533 svn_wc__db_wcroot_t *wcroot,
8534 const char *local_relpath,
8535 apr_pool_t *scratch_pool)
8536 {
8537 struct op_delete_many_baton_t *odmb = baton;
8538 struct op_delete_baton_t odb;
8539 int i;
8540 apr_pool_t *iterpool;
8541
8542 odb.moved_to_relpath = NULL;
8543 odb.conflict = NULL;
8544 odb.work_items = NULL;
8545 odb.delete_dir_externals = odmb->delete_dir_externals;
8546 odb.notify = TRUE;
8547
8548 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8549 iterpool = svn_pool_create(scratch_pool);
8550 for (i = 0; i < odmb->rel_targets->nelts; i++)
8551 {
8552 const char *target_relpath = APR_ARRAY_IDX(odmb->rel_targets, i,
8553 const char *);
8554
8555
8556 svn_pool_clear(iterpool);
8557 SVN_ERR(delete_node(&odb, wcroot, target_relpath, iterpool));
8558 }
8559 svn_pool_destroy(iterpool);
8560
8561 SVN_ERR(add_work_items(wcroot->sdb, odmb->work_items, scratch_pool));
8562
8563 return SVN_NO_ERROR;
8564 }
8565
8566
8567 static svn_error_t *
do_delete_notify(void * baton,svn_wc__db_wcroot_t * wcroot,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)8568 do_delete_notify(void *baton,
8569 svn_wc__db_wcroot_t *wcroot,
8570 svn_cancel_func_t cancel_func,
8571 void *cancel_baton,
8572 svn_wc_notify_func2_t notify_func,
8573 void *notify_baton,
8574 apr_pool_t *scratch_pool)
8575 {
8576 svn_sqlite__stmt_t *stmt;
8577 svn_boolean_t have_row;
8578 apr_pool_t *iterpool;
8579
8580 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8581 STMT_SELECT_DELETE_LIST));
8582 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8583
8584 iterpool = svn_pool_create(scratch_pool);
8585 while (have_row)
8586 {
8587 const char *notify_relpath;
8588 const char *notify_abspath;
8589
8590 svn_pool_clear(iterpool);
8591
8592 notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8593 notify_abspath = svn_dirent_join(wcroot->abspath,
8594 notify_relpath,
8595 iterpool);
8596
8597 notify_func(notify_baton,
8598 svn_wc_create_notify(notify_abspath,
8599 svn_wc_notify_delete,
8600 iterpool),
8601 iterpool);
8602
8603 SVN_ERR(svn_sqlite__step(&have_row, stmt));
8604 }
8605 svn_pool_destroy(iterpool);
8606
8607 SVN_ERR(svn_sqlite__reset(stmt));
8608
8609 /* We only allow cancellation after notification for all deleted nodes
8610 * has happened. The nodes are already deleted so we should notify for
8611 * all of them. */
8612 if (cancel_func)
8613 SVN_ERR(cancel_func(cancel_baton));
8614
8615 return SVN_NO_ERROR;
8616 }
8617
8618
8619 svn_error_t *
svn_wc__db_op_delete(svn_wc__db_t * db,const char * local_abspath,const char * moved_to_abspath,svn_boolean_t delete_dir_externals,svn_skel_t * conflict,svn_skel_t * work_items,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)8620 svn_wc__db_op_delete(svn_wc__db_t *db,
8621 const char *local_abspath,
8622 const char *moved_to_abspath,
8623 svn_boolean_t delete_dir_externals,
8624 svn_skel_t *conflict,
8625 svn_skel_t *work_items,
8626 svn_cancel_func_t cancel_func,
8627 void *cancel_baton,
8628 svn_wc_notify_func2_t notify_func,
8629 void *notify_baton,
8630 apr_pool_t *scratch_pool)
8631 {
8632 svn_wc__db_wcroot_t *wcroot;
8633 svn_wc__db_wcroot_t *moved_to_wcroot;
8634 const char *local_relpath;
8635 const char *moved_to_relpath;
8636 struct op_delete_baton_t odb;
8637
8638 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8639
8640 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8641 db, local_abspath,
8642 scratch_pool, scratch_pool));
8643 VERIFY_USABLE_WCROOT(wcroot);
8644
8645 if (moved_to_abspath)
8646 {
8647 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&moved_to_wcroot,
8648 &moved_to_relpath,
8649 db, moved_to_abspath,
8650 scratch_pool,
8651 scratch_pool));
8652 VERIFY_USABLE_WCROOT(moved_to_wcroot);
8653
8654 if (strcmp(wcroot->abspath, moved_to_wcroot->abspath) != 0)
8655 return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
8656 _("Cannot move '%s' to '%s' because they "
8657 "are not in the same working copy"),
8658 svn_dirent_local_style(local_abspath,
8659 scratch_pool),
8660 svn_dirent_local_style(moved_to_abspath,
8661 scratch_pool));
8662 }
8663 else
8664 moved_to_relpath = NULL;
8665
8666 odb.moved_to_relpath = moved_to_relpath;
8667 odb.conflict = conflict;
8668 odb.work_items = work_items;
8669 odb.delete_dir_externals = delete_dir_externals;
8670
8671 if (notify_func)
8672 {
8673 /* Perform the deletion operation (transactionally), perform any
8674 notifications necessary, and then clean out our temporary tables. */
8675 odb.notify = TRUE;
8676 SVN_ERR(with_finalization(wcroot, local_relpath,
8677 op_delete_txn, &odb,
8678 do_delete_notify, NULL,
8679 cancel_func, cancel_baton,
8680 notify_func, notify_baton,
8681 STMT_FINALIZE_DELETE,
8682 scratch_pool));
8683 }
8684 else
8685 {
8686 /* Avoid the trigger work */
8687 odb.notify = FALSE;
8688 SVN_WC__DB_WITH_TXN(
8689 delete_node(&odb, wcroot, local_relpath, scratch_pool),
8690 wcroot);
8691 }
8692
8693 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
8694 scratch_pool));
8695
8696 return SVN_NO_ERROR;
8697 }
8698
8699
8700 svn_error_t *
svn_wc__db_op_delete_many(svn_wc__db_t * db,apr_array_header_t * targets,svn_boolean_t delete_dir_externals,const svn_skel_t * work_items,svn_cancel_func_t cancel_func,void * cancel_baton,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)8701 svn_wc__db_op_delete_many(svn_wc__db_t *db,
8702 apr_array_header_t *targets,
8703 svn_boolean_t delete_dir_externals,
8704 const svn_skel_t *work_items,
8705 svn_cancel_func_t cancel_func,
8706 void *cancel_baton,
8707 svn_wc_notify_func2_t notify_func,
8708 void *notify_baton,
8709 apr_pool_t *scratch_pool)
8710 {
8711 svn_wc__db_wcroot_t *wcroot;
8712 const char *local_relpath;
8713 struct op_delete_many_baton_t odmb;
8714 int i;
8715 apr_pool_t *iterpool;
8716
8717 odmb.rel_targets = apr_array_make(scratch_pool, targets->nelts,
8718 sizeof(const char *));
8719 odmb.work_items = work_items;
8720 odmb.delete_dir_externals = delete_dir_externals;
8721 iterpool = svn_pool_create(scratch_pool);
8722 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8723 db,
8724 APR_ARRAY_IDX(targets, 0,
8725 const char *),
8726 scratch_pool, iterpool));
8727 VERIFY_USABLE_WCROOT(wcroot);
8728 for (i = 0; i < targets->nelts; i++)
8729 {
8730 const char *local_abspath = APR_ARRAY_IDX(targets, i, const char*);
8731 svn_wc__db_wcroot_t *target_wcroot;
8732
8733 svn_pool_clear(iterpool);
8734
8735 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&target_wcroot,
8736 &local_relpath, db,
8737 APR_ARRAY_IDX(targets, i,
8738 const char *),
8739 scratch_pool, iterpool));
8740 VERIFY_USABLE_WCROOT(target_wcroot);
8741 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8742
8743 /* Assert that all targets are within the same working copy. */
8744 SVN_ERR_ASSERT(wcroot->wc_id == target_wcroot->wc_id);
8745
8746 APR_ARRAY_PUSH(odmb.rel_targets, const char *) = local_relpath;
8747 SVN_ERR(flush_entries(target_wcroot, local_abspath, svn_depth_infinity,
8748 iterpool));
8749
8750 }
8751 svn_pool_destroy(iterpool);
8752
8753 /* Perform the deletion operation (transactionally), perform any
8754 notifications necessary, and then clean out our temporary tables. */
8755 return svn_error_trace(with_finalization(wcroot, wcroot->abspath,
8756 op_delete_many_txn, &odmb,
8757 do_delete_notify, NULL,
8758 cancel_func, cancel_baton,
8759 notify_func, notify_baton,
8760 STMT_FINALIZE_DELETE,
8761 scratch_pool));
8762 }
8763
8764 /* Helper function for read_info() to provide better diagnostics than just
8765 asserting.
8766
8767 ### BH: Yes this code is ugly, and that is why I only introduce it in
8768 ### read_info(). But we really need something to determine the root cause
8769 ### of this problem to diagnose why TortoiseSVN users were seeing all those
8770 ### assertions.
8771
8772 Adds an error to the *err chain if invalid values are encountered. In that
8773 case the value is set to the first value in the map, assuming that caller
8774 will just return the combined error.
8775 */
8776 static int
column_token_err(svn_error_t ** err,svn_sqlite__stmt_t * stmt,int column,const svn_token_map_t * map)8777 column_token_err(svn_error_t **err,
8778 svn_sqlite__stmt_t *stmt,
8779 int column,
8780 const svn_token_map_t *map)
8781 {
8782 svn_error_t *err2;
8783 const char *word = svn_sqlite__column_text(stmt, column, NULL);
8784 int value;
8785
8786 /* svn_token__from_word_err() handles NULL for us */
8787 err2 = svn_token__from_word_err(&value, map, word);
8788
8789 if (err2)
8790 {
8791 *err = svn_error_compose_create(
8792 *err,
8793 svn_error_createf(
8794 SVN_ERR_WC_CORRUPT, err2,
8795 _("Encountered invalid node state in column %d of "
8796 "info query to working copy database"),
8797 column));
8798 value = map[0].val;
8799 }
8800
8801 return value;
8802 }
8803
8804 /* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of
8805 DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */
8806 static svn_error_t *
read_info(svn_wc__db_status_t * status,svn_node_kind_t * kind,svn_revnum_t * revision,const char ** repos_relpath,apr_int64_t * repos_id,svn_revnum_t * changed_rev,apr_time_t * changed_date,const char ** changed_author,svn_depth_t * depth,const svn_checksum_t ** checksum,const char ** target,const char ** original_repos_relpath,apr_int64_t * original_repos_id,svn_revnum_t * original_revision,svn_wc__db_lock_t ** lock,svn_filesize_t * recorded_size,apr_time_t * recorded_time,const char ** changelist,svn_boolean_t * conflicted,svn_boolean_t * op_root,svn_boolean_t * had_props,svn_boolean_t * props_mod,svn_boolean_t * have_base,svn_boolean_t * have_more_work,svn_boolean_t * have_work,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)8807 read_info(svn_wc__db_status_t *status,
8808 svn_node_kind_t *kind,
8809 svn_revnum_t *revision,
8810 const char **repos_relpath,
8811 apr_int64_t *repos_id,
8812 svn_revnum_t *changed_rev,
8813 apr_time_t *changed_date,
8814 const char **changed_author,
8815 svn_depth_t *depth,
8816 const svn_checksum_t **checksum,
8817 const char **target,
8818 const char **original_repos_relpath,
8819 apr_int64_t *original_repos_id,
8820 svn_revnum_t *original_revision,
8821 svn_wc__db_lock_t **lock,
8822 svn_filesize_t *recorded_size,
8823 apr_time_t *recorded_time,
8824 const char **changelist,
8825 svn_boolean_t *conflicted,
8826 svn_boolean_t *op_root,
8827 svn_boolean_t *had_props,
8828 svn_boolean_t *props_mod,
8829 svn_boolean_t *have_base,
8830 svn_boolean_t *have_more_work,
8831 svn_boolean_t *have_work,
8832 svn_wc__db_wcroot_t *wcroot,
8833 const char *local_relpath,
8834 apr_pool_t *result_pool,
8835 apr_pool_t *scratch_pool)
8836 {
8837 svn_sqlite__stmt_t *stmt_info;
8838 svn_sqlite__stmt_t *stmt_act;
8839 svn_boolean_t have_info;
8840 svn_boolean_t have_act;
8841 svn_error_t *err = NULL;
8842
8843 /* Obtain the most likely to exist record first, to make sure we don't
8844 have to obtain the SQLite read-lock multiple times */
8845 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
8846 lock ? STMT_SELECT_NODE_INFO_WITH_LOCK
8847 : STMT_SELECT_NODE_INFO));
8848 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
8849 SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
8850
8851 if (changelist || conflicted || props_mod)
8852 {
8853 SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
8854 STMT_SELECT_ACTUAL_NODE));
8855 SVN_ERR(svn_sqlite__bindf(stmt_act, "is", wcroot->wc_id, local_relpath));
8856 SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
8857 }
8858 else
8859 {
8860 have_act = FALSE;
8861 stmt_act = NULL;
8862 }
8863
8864 if (have_info)
8865 {
8866 int op_depth;
8867 svn_node_kind_t node_kind;
8868
8869 op_depth = svn_sqlite__column_int(stmt_info, 0);
8870 node_kind = column_token_err(&err, stmt_info, 4, kind_map);
8871
8872 if (status)
8873 {
8874 *status = column_token_err(&err, stmt_info, 3, presence_map);
8875
8876 if (op_depth != 0) /* WORKING */
8877 err = svn_error_compose_create(err,
8878 convert_to_working_status(status,
8879 *status));
8880 }
8881 if (kind)
8882 {
8883 *kind = node_kind;
8884 }
8885 if (op_depth != 0)
8886 {
8887 if (repos_id)
8888 *repos_id = INVALID_REPOS_ID;
8889 if (revision)
8890 *revision = SVN_INVALID_REVNUM;
8891 if (repos_relpath)
8892 /* Our path is implied by our parent somewhere up the tree.
8893 With the NULL value and status, the caller will know to
8894 search up the tree for the base of our path. */
8895 *repos_relpath = NULL;
8896 }
8897 else
8898 {
8899 /* Fetch repository information. If we have a
8900 WORKING_NODE (and have been added), then the repository
8901 we're being added to will be dependent upon a parent. The
8902 caller can scan upwards to locate the repository. */
8903 repos_location_from_columns(repos_id, revision, repos_relpath,
8904 stmt_info, 1, 5, 2, result_pool);
8905 }
8906 if (changed_rev)
8907 {
8908 *changed_rev = svn_sqlite__column_revnum(stmt_info, 8);
8909 }
8910 if (changed_date)
8911 {
8912 *changed_date = svn_sqlite__column_int64(stmt_info, 9);
8913 }
8914 if (changed_author)
8915 {
8916 *changed_author = svn_sqlite__column_text(stmt_info, 10,
8917 result_pool);
8918 }
8919 if (recorded_time)
8920 {
8921 *recorded_time = svn_sqlite__column_int64(stmt_info, 13);
8922 }
8923 if (depth)
8924 {
8925 if (node_kind != svn_node_dir)
8926 *depth = svn_depth_unknown;
8927 else if (svn_sqlite__column_is_null(stmt_info, 11))
8928 *depth = svn_depth_unknown;
8929 else
8930 *depth = column_token_err(&err, stmt_info, 11, depth_map);
8931 }
8932 if (checksum)
8933 {
8934 if (node_kind != svn_node_file)
8935 {
8936 *checksum = NULL;
8937 }
8938 else
8939 {
8940
8941 err = svn_error_compose_create(
8942 err, svn_sqlite__column_checksum(checksum, stmt_info, 6,
8943 result_pool));
8944 }
8945 }
8946 if (recorded_size)
8947 {
8948 *recorded_size = get_recorded_size(stmt_info, 7);
8949 }
8950 if (target)
8951 {
8952 if (node_kind != svn_node_symlink)
8953 *target = NULL;
8954 else
8955 *target = svn_sqlite__column_text(stmt_info, 12, result_pool);
8956 }
8957 if (changelist)
8958 {
8959 if (have_act)
8960 *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8961 else
8962 *changelist = NULL;
8963 }
8964 if (op_depth == 0)
8965 {
8966 if (original_repos_id)
8967 *original_repos_id = INVALID_REPOS_ID;
8968 if (original_revision)
8969 *original_revision = SVN_INVALID_REVNUM;
8970 if (original_repos_relpath)
8971 *original_repos_relpath = NULL;
8972 }
8973 else
8974 {
8975 repos_location_from_columns(original_repos_id,
8976 original_revision,
8977 original_repos_relpath,
8978 stmt_info, 1, 5, 2, result_pool);
8979 }
8980 if (props_mod)
8981 {
8982 *props_mod = have_act && !svn_sqlite__column_is_null(stmt_act, 1);
8983 }
8984 if (had_props)
8985 {
8986 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt_info, 14);
8987 }
8988 if (conflicted)
8989 {
8990 if (have_act)
8991 {
8992 *conflicted =
8993 !svn_sqlite__column_is_null(stmt_act, 2); /* conflict_data */
8994 }
8995 else
8996 *conflicted = FALSE;
8997 }
8998
8999 if (lock)
9000 {
9001 if (op_depth != 0)
9002 *lock = NULL;
9003 else
9004 *lock = lock_from_columns(stmt_info, 17, 18, 19, 20, result_pool);
9005 }
9006
9007 if (have_work)
9008 *have_work = (op_depth != 0);
9009
9010 if (op_root)
9011 {
9012 *op_root = ((op_depth > 0)
9013 && (op_depth == relpath_depth(local_relpath)));
9014 }
9015
9016 if (have_base || have_more_work)
9017 {
9018 if (have_more_work)
9019 *have_more_work = FALSE;
9020
9021 while (!err && op_depth != 0)
9022 {
9023 err = svn_sqlite__step(&have_info, stmt_info);
9024
9025 if (err || !have_info)
9026 break;
9027
9028 op_depth = svn_sqlite__column_int(stmt_info, 0);
9029
9030 if (have_more_work)
9031 {
9032 if (op_depth > 0)
9033 *have_more_work = TRUE;
9034
9035 if (!have_base)
9036 break;
9037 }
9038 }
9039
9040 if (have_base)
9041 *have_base = (op_depth == 0);
9042 }
9043 }
9044 else if (have_act)
9045 {
9046 /* A row in ACTUAL_NODE should never exist without a corresponding
9047 node in BASE_NODE and/or WORKING_NODE unless it flags a tree conflict. */
9048 if (svn_sqlite__column_is_null(stmt_act, 2)) /* conflict_data */
9049 err = svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
9050 _("Corrupt data for '%s'"),
9051 path_for_error_message(wcroot, local_relpath,
9052 scratch_pool));
9053 /* ### What should we return? Should we have a separate
9054 function for reading actual-only nodes? */
9055
9056 /* As a safety measure, until we decide if we want to use
9057 read_info for actual-only nodes, make sure the caller asked
9058 for the conflict status. */
9059 SVN_ERR_ASSERT(conflicted);
9060
9061 if (status)
9062 *status = svn_wc__db_status_normal; /* What! No it's not! */
9063 if (kind)
9064 *kind = svn_node_unknown;
9065 if (revision)
9066 *revision = SVN_INVALID_REVNUM;
9067 if (repos_relpath)
9068 *repos_relpath = NULL;
9069 if (repos_id)
9070 *repos_id = INVALID_REPOS_ID;
9071 if (changed_rev)
9072 *changed_rev = SVN_INVALID_REVNUM;
9073 if (changed_date)
9074 *changed_date = 0;
9075 if (depth)
9076 *depth = svn_depth_unknown;
9077 if (checksum)
9078 *checksum = NULL;
9079 if (target)
9080 *target = NULL;
9081 if (original_repos_relpath)
9082 *original_repos_relpath = NULL;
9083 if (original_repos_id)
9084 *original_repos_id = INVALID_REPOS_ID;
9085 if (original_revision)
9086 *original_revision = SVN_INVALID_REVNUM;
9087 if (lock)
9088 *lock = NULL;
9089 if (recorded_size)
9090 *recorded_size = 0;
9091 if (recorded_time)
9092 *recorded_time = 0;
9093 if (changelist)
9094 *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
9095 if (op_root)
9096 *op_root = FALSE;
9097 if (had_props)
9098 *had_props = FALSE;
9099 if (props_mod)
9100 *props_mod = FALSE;
9101 if (conflicted)
9102 *conflicted = TRUE;
9103 if (have_base)
9104 *have_base = FALSE;
9105 if (have_more_work)
9106 *have_more_work = FALSE;
9107 if (have_work)
9108 *have_work = FALSE;
9109 }
9110 else
9111 {
9112 err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
9113 _("The node '%s' was not found."),
9114 path_for_error_message(wcroot, local_relpath,
9115 scratch_pool));
9116 }
9117
9118 if (stmt_act != NULL)
9119 err = svn_error_compose_create(err, svn_sqlite__reset(stmt_act));
9120
9121 if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
9122 err = svn_error_quick_wrapf(err, _("Error reading node '%s'"),
9123 local_relpath);
9124
9125 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info)));
9126
9127 return SVN_NO_ERROR;
9128 }
9129
9130
9131 svn_error_t *
svn_wc__db_read_info_internal(svn_wc__db_status_t * status,svn_node_kind_t * kind,svn_revnum_t * revision,const char ** repos_relpath,apr_int64_t * repos_id,svn_revnum_t * changed_rev,apr_time_t * changed_date,const char ** changed_author,svn_depth_t * depth,const svn_checksum_t ** checksum,const char ** target,const char ** original_repos_relpath,apr_int64_t * original_repos_id,svn_revnum_t * original_revision,svn_wc__db_lock_t ** lock,svn_filesize_t * recorded_size,apr_time_t * recorded_time,const char ** changelist,svn_boolean_t * conflicted,svn_boolean_t * op_root,svn_boolean_t * had_props,svn_boolean_t * props_mod,svn_boolean_t * have_base,svn_boolean_t * have_more_work,svn_boolean_t * have_work,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9132 svn_wc__db_read_info_internal(svn_wc__db_status_t *status,
9133 svn_node_kind_t *kind,
9134 svn_revnum_t *revision,
9135 const char **repos_relpath,
9136 apr_int64_t *repos_id,
9137 svn_revnum_t *changed_rev,
9138 apr_time_t *changed_date,
9139 const char **changed_author,
9140 svn_depth_t *depth,
9141 const svn_checksum_t **checksum,
9142 const char **target,
9143 const char **original_repos_relpath,
9144 apr_int64_t *original_repos_id,
9145 svn_revnum_t *original_revision,
9146 svn_wc__db_lock_t **lock,
9147 svn_filesize_t *recorded_size,
9148 apr_time_t *recorded_time,
9149 const char **changelist,
9150 svn_boolean_t *conflicted,
9151 svn_boolean_t *op_root,
9152 svn_boolean_t *had_props,
9153 svn_boolean_t *props_mod,
9154 svn_boolean_t *have_base,
9155 svn_boolean_t *have_more_work,
9156 svn_boolean_t *have_work,
9157 svn_wc__db_wcroot_t *wcroot,
9158 const char *local_relpath,
9159 apr_pool_t *result_pool,
9160 apr_pool_t *scratch_pool)
9161 {
9162 return svn_error_trace(
9163 read_info(status, kind, revision, repos_relpath, repos_id,
9164 changed_rev, changed_date, changed_author,
9165 depth, checksum, target, original_repos_relpath,
9166 original_repos_id, original_revision, lock,
9167 recorded_size, recorded_time, changelist, conflicted,
9168 op_root, had_props, props_mod,
9169 have_base, have_more_work, have_work,
9170 wcroot, local_relpath, result_pool, scratch_pool));
9171 }
9172
9173
9174 svn_error_t *
svn_wc__db_read_info(svn_wc__db_status_t * status,svn_node_kind_t * kind,svn_revnum_t * revision,const char ** repos_relpath,const char ** repos_root_url,const char ** repos_uuid,svn_revnum_t * changed_rev,apr_time_t * changed_date,const char ** changed_author,svn_depth_t * depth,const svn_checksum_t ** checksum,const char ** target,const char ** original_repos_relpath,const char ** original_root_url,const char ** original_uuid,svn_revnum_t * original_revision,svn_wc__db_lock_t ** lock,svn_filesize_t * recorded_size,apr_time_t * recorded_time,const char ** changelist,svn_boolean_t * conflicted,svn_boolean_t * op_root,svn_boolean_t * have_props,svn_boolean_t * props_mod,svn_boolean_t * have_base,svn_boolean_t * have_more_work,svn_boolean_t * have_work,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9175 svn_wc__db_read_info(svn_wc__db_status_t *status,
9176 svn_node_kind_t *kind,
9177 svn_revnum_t *revision,
9178 const char **repos_relpath,
9179 const char **repos_root_url,
9180 const char **repos_uuid,
9181 svn_revnum_t *changed_rev,
9182 apr_time_t *changed_date,
9183 const char **changed_author,
9184 svn_depth_t *depth,
9185 const svn_checksum_t **checksum,
9186 const char **target,
9187 const char **original_repos_relpath,
9188 const char **original_root_url,
9189 const char **original_uuid,
9190 svn_revnum_t *original_revision,
9191 svn_wc__db_lock_t **lock,
9192 svn_filesize_t *recorded_size,
9193 apr_time_t *recorded_time,
9194 const char **changelist,
9195 svn_boolean_t *conflicted,
9196 svn_boolean_t *op_root,
9197 svn_boolean_t *have_props,
9198 svn_boolean_t *props_mod,
9199 svn_boolean_t *have_base,
9200 svn_boolean_t *have_more_work,
9201 svn_boolean_t *have_work,
9202 svn_wc__db_t *db,
9203 const char *local_abspath,
9204 apr_pool_t *result_pool,
9205 apr_pool_t *scratch_pool)
9206 {
9207 svn_wc__db_wcroot_t *wcroot;
9208 const char *local_relpath;
9209 apr_int64_t repos_id, original_repos_id;
9210
9211 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9212
9213 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9214 local_abspath, scratch_pool, scratch_pool));
9215 VERIFY_USABLE_WCROOT(wcroot);
9216
9217 SVN_WC__DB_WITH_TXN4(
9218 read_info(status, kind, revision, repos_relpath, &repos_id,
9219 changed_rev, changed_date, changed_author,
9220 depth, checksum, target, original_repos_relpath,
9221 &original_repos_id, original_revision, lock,
9222 recorded_size, recorded_time, changelist, conflicted,
9223 op_root, have_props, props_mod,
9224 have_base, have_more_work, have_work,
9225 wcroot, local_relpath, result_pool, scratch_pool),
9226 svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
9227 wcroot, repos_id, result_pool),
9228 svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
9229 wcroot, original_repos_id,
9230 result_pool),
9231 SVN_NO_ERROR,
9232 wcroot);
9233
9234 return SVN_NO_ERROR;
9235 }
9236
9237 static svn_error_t *
9238 is_wclocked(svn_boolean_t *locked,
9239 svn_wc__db_wcroot_t *wcroot,
9240 const char *dir_relpath,
9241 apr_pool_t *scratch_pool);
9242
9243 /* Helper for read_children_info and single variant */
9244 static svn_error_t *
find_conflict_descendants(svn_boolean_t * conflict_exists,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)9245 find_conflict_descendants(svn_boolean_t *conflict_exists,
9246 svn_wc__db_wcroot_t *wcroot,
9247 const char *local_relpath,
9248 apr_pool_t *scratch_pool)
9249 {
9250 svn_sqlite__stmt_t *stmt;
9251
9252 /* Only used on files, so certainly not wcroot*/
9253 assert(local_relpath[0] != '\0');
9254
9255 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9256 STMT_FIND_CONFLICT_DESCENDANT));
9257
9258 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9259 SVN_ERR(svn_sqlite__step(conflict_exists, stmt));
9260
9261 return svn_error_trace(svn_sqlite__reset(stmt));
9262 }
9263
9264 /* What we really want to store about a node. This relies on the
9265 offset of svn_wc__db_info_t being zero. */
9266 struct read_children_info_item_t
9267 {
9268 struct svn_wc__db_info_t info;
9269 int op_depth;
9270 int nr_layers;
9271 svn_boolean_t was_dir;
9272 };
9273
9274 /* Implementation of svn_wc__db_read_children_info */
9275 static svn_error_t *
read_children_info(svn_wc__db_wcroot_t * wcroot,const char * dir_relpath,apr_hash_t * conflicts,apr_hash_t * nodes,svn_boolean_t base_tree_only,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9276 read_children_info(svn_wc__db_wcroot_t *wcroot,
9277 const char *dir_relpath,
9278 apr_hash_t *conflicts,
9279 apr_hash_t *nodes,
9280 svn_boolean_t base_tree_only,
9281 apr_pool_t *result_pool,
9282 apr_pool_t *scratch_pool)
9283 {
9284 svn_sqlite__stmt_t *stmt;
9285 svn_boolean_t have_row;
9286 const char *repos_root_url = NULL;
9287 const char *repos_uuid = NULL;
9288 apr_int64_t last_repos_id = INVALID_REPOS_ID;
9289 const char *last_repos_root_url = NULL;
9290
9291 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9292 (base_tree_only
9293 ? STMT_SELECT_BASE_NODE_CHILDREN_INFO
9294 : STMT_SELECT_NODE_CHILDREN_INFO)));
9295 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9296 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9297
9298 while (have_row)
9299 {
9300 /* CHILD item points to what we have about the node. We only provide
9301 CHILD->item to our caller. */
9302 struct read_children_info_item_t *child_item;
9303 const char *child_relpath = svn_sqlite__column_text(stmt, 19, NULL);
9304 const char *name = svn_relpath_basename(child_relpath, NULL);
9305 svn_error_t *err;
9306 int op_depth;
9307 svn_boolean_t new_child;
9308
9309 child_item = (base_tree_only ? NULL : svn_hash_gets(nodes, name));
9310 if (child_item)
9311 new_child = FALSE;
9312 else
9313 {
9314 child_item = apr_pcalloc(result_pool, sizeof(*child_item));
9315 new_child = TRUE;
9316 }
9317
9318 op_depth = svn_sqlite__column_int(stmt, 0);
9319
9320 /* Do we have new or better information? */
9321 if (new_child)
9322 {
9323 struct svn_wc__db_info_t *child = &child_item->info;
9324 child_item->op_depth = op_depth;
9325
9326 child->kind = svn_sqlite__column_token(stmt, 4, kind_map);
9327
9328 child->status = svn_sqlite__column_token(stmt, 3, presence_map);
9329 if (op_depth != 0)
9330 {
9331 if (child->status == svn_wc__db_status_incomplete)
9332 child->incomplete = TRUE;
9333 err = convert_to_working_status(&child->status, child->status);
9334 if (err)
9335 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9336 }
9337
9338 if (op_depth != 0)
9339 child->revnum = SVN_INVALID_REVNUM;
9340 else
9341 child->revnum = svn_sqlite__column_revnum(stmt, 5);
9342
9343 if (op_depth != 0)
9344 child->repos_relpath = NULL;
9345 else
9346 child->repos_relpath = svn_sqlite__column_text(stmt, 2,
9347 result_pool);
9348
9349 if (op_depth != 0 || svn_sqlite__column_is_null(stmt, 1))
9350 {
9351 child->repos_root_url = NULL;
9352 child->repos_uuid = NULL;
9353 }
9354 else
9355 {
9356 apr_int64_t repos_id = svn_sqlite__column_int64(stmt, 1);
9357 if (!repos_root_url ||
9358 (last_repos_id != INVALID_REPOS_ID &&
9359 repos_id != last_repos_id))
9360 {
9361 last_repos_root_url = repos_root_url;
9362 err = svn_wc__db_fetch_repos_info(&repos_root_url,
9363 &repos_uuid,
9364 wcroot, repos_id,
9365 result_pool);
9366 if (err)
9367 SVN_ERR(svn_error_compose_create(err,
9368 svn_sqlite__reset(stmt)));
9369 }
9370
9371 if (last_repos_id == INVALID_REPOS_ID)
9372 last_repos_id = repos_id;
9373
9374 /* Assume working copy is all one repos_id so that a
9375 single cached value is sufficient. */
9376 if (repos_id != last_repos_id)
9377 {
9378 err= svn_error_createf(
9379 SVN_ERR_WC_DB_ERROR, NULL,
9380 _("The node '%s' comes from unexpected repository "
9381 "'%s', expected '%s'; if this node is a file "
9382 "external using the correct URL in the external "
9383 "definition can fix the problem, see issue #4087"),
9384 child_relpath, repos_root_url, last_repos_root_url);
9385 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
9386 }
9387 child->repos_root_url = repos_root_url;
9388 child->repos_uuid = repos_uuid;
9389 }
9390
9391 child->changed_rev = svn_sqlite__column_revnum(stmt, 8);
9392
9393 child->changed_date = svn_sqlite__column_int64(stmt, 9);
9394
9395 child->changed_author = svn_sqlite__column_text(stmt, 10,
9396 result_pool);
9397
9398 if (child->kind != svn_node_dir)
9399 child->depth = svn_depth_unknown;
9400 else
9401 {
9402 child->has_descendants = TRUE;
9403 child_item->was_dir = TRUE;
9404 child->depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
9405 svn_depth_unknown);
9406 if (new_child)
9407 {
9408 err = is_wclocked(&child->locked, wcroot, child_relpath,
9409 scratch_pool);
9410
9411 if (err)
9412 SVN_ERR(svn_error_compose_create(err,
9413 svn_sqlite__reset(stmt)));
9414 }
9415 }
9416
9417 child->recorded_time = svn_sqlite__column_int64(stmt, 13);
9418 child->recorded_size = get_recorded_size(stmt, 7);
9419 child->has_checksum = !svn_sqlite__column_is_null(stmt, 6);
9420 child->copied = op_depth > 0 && !svn_sqlite__column_is_null(stmt, 2);
9421 child->had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
9422 #ifdef HAVE_SYMLINK
9423 if (child->had_props)
9424 {
9425 apr_hash_t *properties;
9426 err = svn_sqlite__column_properties(&properties, stmt, 14,
9427 scratch_pool, scratch_pool);
9428 if (err)
9429 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9430
9431 child->special = (child->had_props
9432 && svn_hash_gets(properties, SVN_PROP_SPECIAL));
9433 }
9434 #endif
9435 if (op_depth == 0)
9436 child->op_root = FALSE;
9437 else
9438 child->op_root = (op_depth == relpath_depth(child_relpath));
9439
9440 if (op_depth && child->op_root)
9441 child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
9442
9443 if (new_child)
9444 svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
9445 }
9446 else if (!child_item->was_dir
9447 && svn_sqlite__column_token(stmt, 4, kind_map) == svn_node_dir)
9448 {
9449 child_item->was_dir = TRUE;
9450
9451 err = find_conflict_descendants(&child_item->info.has_descendants,
9452 wcroot, child_relpath,
9453 scratch_pool);
9454 if (err)
9455 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9456 }
9457
9458 if (op_depth == 0)
9459 {
9460 child_item->info.have_base = TRUE;
9461
9462 /* Get the lock info, available only at op_depth 0. */
9463 child_item->info.lock = lock_from_columns(stmt, 15, 16, 17, 18,
9464 result_pool);
9465
9466 /* FILE_EXTERNAL flag only on op_depth 0. */
9467 child_item->info.file_external = svn_sqlite__column_boolean(stmt,
9468 22);
9469 }
9470 else
9471 {
9472 const char *moved_to_relpath;
9473
9474 child_item->nr_layers++;
9475 child_item->info.have_more_work = (child_item->nr_layers > 1);
9476
9477
9478 /* A local_relpath can be moved multiple times at different op
9479 depths and it really depends on the caller what is interesting.
9480 We provide a simple linked list with the moved_from information */
9481
9482 moved_to_relpath = svn_sqlite__column_text(stmt, 21, NULL);
9483 if (moved_to_relpath)
9484 {
9485 struct svn_wc__db_moved_to_info_t *moved_to;
9486 struct svn_wc__db_moved_to_info_t **next;
9487 const char *shadow_op_relpath;
9488
9489 moved_to = apr_pcalloc(result_pool, sizeof(*moved_to));
9490 moved_to->moved_to_abspath = svn_dirent_join(wcroot->abspath,
9491 moved_to_relpath,
9492 result_pool);
9493
9494 shadow_op_relpath = svn_relpath_prefix(child_relpath, op_depth,
9495 scratch_pool);
9496
9497 moved_to->shadow_op_root_abspath =
9498 svn_dirent_join(wcroot->abspath, shadow_op_relpath,
9499 result_pool);
9500
9501 next = &child_item->info.moved_to;
9502
9503 while (*next &&
9504 0 < strcmp((*next)->shadow_op_root_abspath,
9505 moved_to->shadow_op_root_abspath))
9506 next = &((*next)->next);
9507
9508 moved_to->next = *next;
9509 *next = moved_to;
9510 }
9511 }
9512
9513 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9514 }
9515
9516 SVN_ERR(svn_sqlite__reset(stmt));
9517
9518 if (!base_tree_only)
9519 {
9520 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9521 STMT_SELECT_ACTUAL_CHILDREN_INFO));
9522 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9523 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9524
9525 while (have_row)
9526 {
9527 struct read_children_info_item_t *child_item;
9528 struct svn_wc__db_info_t *child;
9529 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9530 const char *name = svn_relpath_basename(child_relpath, NULL);
9531
9532 child_item = svn_hash_gets(nodes, name);
9533 if (!child_item)
9534 {
9535 child_item = apr_pcalloc(result_pool, sizeof(*child_item));
9536 child_item->info.status = svn_wc__db_status_not_present;
9537 }
9538
9539 child = &child_item->info;
9540
9541 child->changelist = svn_sqlite__column_text(stmt, 1, result_pool);
9542
9543 child->props_mod = !svn_sqlite__column_is_null(stmt, 2);
9544 #ifdef HAVE_SYMLINK
9545 if (child->props_mod)
9546 {
9547 svn_error_t *err;
9548 apr_hash_t *properties;
9549
9550 err = svn_sqlite__column_properties(&properties, stmt, 2,
9551 scratch_pool, scratch_pool);
9552 if (err)
9553 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9554 child->special = (NULL != svn_hash_gets(properties,
9555 SVN_PROP_SPECIAL));
9556 }
9557 #endif
9558
9559 /* conflict */
9560 child->conflicted = !svn_sqlite__column_is_null(stmt, 3);
9561
9562 if (child->conflicted)
9563 svn_hash_sets(conflicts, apr_pstrdup(result_pool, name), "");
9564
9565 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9566 }
9567
9568 SVN_ERR(svn_sqlite__reset(stmt));
9569 }
9570
9571 return SVN_NO_ERROR;
9572 }
9573
9574 svn_error_t *
svn_wc__db_read_children_info(apr_hash_t ** nodes,apr_hash_t ** conflicts,svn_wc__db_t * db,const char * dir_abspath,svn_boolean_t base_tree_only,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9575 svn_wc__db_read_children_info(apr_hash_t **nodes,
9576 apr_hash_t **conflicts,
9577 svn_wc__db_t *db,
9578 const char *dir_abspath,
9579 svn_boolean_t base_tree_only,
9580 apr_pool_t *result_pool,
9581 apr_pool_t *scratch_pool)
9582 {
9583 svn_wc__db_wcroot_t *wcroot;
9584 const char *dir_relpath;
9585
9586 *conflicts = apr_hash_make(result_pool);
9587 *nodes = apr_hash_make(result_pool);
9588 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9589
9590 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9591 dir_abspath,
9592 scratch_pool, scratch_pool));
9593 VERIFY_USABLE_WCROOT(wcroot);
9594
9595 SVN_WC__DB_WITH_TXN(
9596 read_children_info(wcroot, dir_relpath, *conflicts, *nodes,
9597 base_tree_only, result_pool, scratch_pool),
9598 wcroot);
9599
9600 return SVN_NO_ERROR;
9601 }
9602
9603 /* Implementation of svn_wc__db_read_single_info.
9604
9605 ### This function is very similar to a lot of code inside
9606 read_children_info, but that performs some tricks to re-use data between
9607 different siblings.
9608
9609 ### We read the same few NODES records a few times via different helper
9610 functions, so this could be optimized bit, but everything is within
9611 a sqlite transaction and all queries are backed by an index, so generally
9612 everything (including the used indexes) should be in the sqlite page cache
9613 after the first query.
9614 */
9615 static svn_error_t *
read_single_info(const struct svn_wc__db_info_t ** info,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_boolean_t base_tree_only,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9616 read_single_info(const struct svn_wc__db_info_t **info,
9617 svn_wc__db_wcroot_t *wcroot,
9618 const char *local_relpath,
9619 svn_boolean_t base_tree_only,
9620 apr_pool_t *result_pool,
9621 apr_pool_t *scratch_pool)
9622 {
9623 struct svn_wc__db_info_t *mtb;
9624 apr_int64_t repos_id;
9625 const svn_checksum_t *checksum;
9626 const char *original_repos_relpath;
9627 svn_boolean_t have_work;
9628 apr_hash_t *properties;
9629
9630 mtb = apr_pcalloc(result_pool, sizeof(*mtb));
9631
9632 if (!base_tree_only)
9633 SVN_ERR(read_info(&mtb->status, &mtb->kind, &mtb->revnum,
9634 &mtb->repos_relpath, &repos_id, &mtb->changed_rev,
9635 &mtb->changed_date, &mtb->changed_author, &mtb->depth,
9636 &checksum, NULL, &original_repos_relpath, NULL, NULL,
9637 &mtb->lock, &mtb->recorded_size, &mtb->recorded_time,
9638 &mtb->changelist, &mtb->conflicted, &mtb->op_root,
9639 &mtb->had_props, &mtb->props_mod, &mtb->have_base,
9640 &mtb->have_more_work, &have_work,
9641 wcroot, local_relpath, result_pool, scratch_pool));
9642 else
9643 {
9644 svn_boolean_t update_root;
9645
9646 have_work = FALSE;
9647 original_repos_relpath = NULL;
9648
9649 SVN_ERR(svn_wc__db_base_get_info_internal(
9650 &mtb->status, &mtb->kind, &mtb->revnum, &mtb->repos_relpath,
9651 &repos_id, &mtb->changed_rev, &mtb->changed_date,
9652 &mtb->changed_author, &mtb->depth, &checksum, NULL,
9653 &mtb->lock, &mtb->had_props, &properties, &update_root,
9654 wcroot, local_relpath, scratch_pool, scratch_pool));
9655
9656 mtb->have_base = TRUE;
9657 mtb->file_external = (update_root && mtb->kind == svn_node_file);
9658 }
9659
9660 /* Query the same rows in the database again for move information */
9661 if (have_work && (mtb->have_base || mtb->have_more_work))
9662 {
9663 svn_sqlite__stmt_t *stmt;
9664 svn_boolean_t have_row;
9665
9666 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9667 STMT_SELECT_MOVED_TO_NODE));
9668 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9669
9670 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9671
9672 while (have_row)
9673 {
9674 struct svn_wc__db_moved_to_info_t *move;
9675 int op_depth = svn_sqlite__column_int(stmt, 0);
9676 const char *moved_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
9677 const char *cur_relpath;
9678
9679 move = apr_pcalloc(result_pool, sizeof(*move));
9680 move->moved_to_abspath = svn_dirent_join(wcroot->abspath,
9681 moved_to_relpath,
9682 result_pool);
9683
9684 cur_relpath = svn_relpath_prefix(local_relpath, op_depth,
9685 scratch_pool);
9686
9687 move->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath,
9688 cur_relpath,
9689 result_pool);
9690
9691 move->next = mtb->moved_to;
9692 mtb->moved_to = move;
9693
9694 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9695 }
9696
9697 SVN_ERR(svn_sqlite__reset(stmt));
9698 }
9699
9700 /* Maybe we have to get some shadowed lock from BASE to make our test suite
9701 happy... (It might be completely unrelated, but...)
9702 This queries the same BASE row again, joined to the lock table */
9703 if (!base_tree_only && mtb->have_base
9704 && (have_work || mtb->kind == svn_node_file))
9705 {
9706 svn_boolean_t update_root;
9707 svn_wc__db_lock_t **lock_arg = NULL;
9708
9709 if (have_work)
9710 lock_arg = &mtb->lock;
9711
9712 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL, NULL,
9713 NULL, NULL, NULL, NULL, NULL,
9714 NULL, lock_arg, NULL, NULL,
9715 &update_root,
9716 wcroot, local_relpath,
9717 result_pool, scratch_pool));
9718
9719 mtb->file_external = (update_root && mtb->kind == svn_node_file);
9720 }
9721
9722 if (mtb->status == svn_wc__db_status_added)
9723 {
9724 svn_wc__db_status_t status;
9725
9726 SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9727 NULL, NULL,
9728 wcroot, local_relpath,
9729 result_pool, scratch_pool));
9730
9731 mtb->moved_here = (status == svn_wc__db_status_moved_here);
9732 mtb->incomplete = (status == svn_wc__db_status_incomplete);
9733 }
9734
9735 #ifdef HAVE_SYMLINK
9736 if (mtb->kind == svn_node_file
9737 && (mtb->had_props || mtb->props_mod
9738 || (base_tree_only && properties)))
9739 {
9740 if (!base_tree_only)
9741 {
9742 if (mtb->props_mod)
9743 SVN_ERR(svn_wc__db_read_props_internal(&properties,
9744 wcroot, local_relpath,
9745 scratch_pool, scratch_pool));
9746 else
9747 SVN_ERR(db_read_pristine_props(&properties, wcroot, local_relpath,
9748 TRUE /* deleted_ok */,
9749 scratch_pool, scratch_pool));
9750 }
9751
9752 mtb->special = (NULL != svn_hash_gets(properties, SVN_PROP_SPECIAL));
9753 }
9754 #endif
9755
9756 mtb->has_checksum = (checksum != NULL);
9757 mtb->copied = (original_repos_relpath != NULL);
9758
9759 SVN_ERR(svn_wc__db_fetch_repos_info(&mtb->repos_root_url, &mtb->repos_uuid,
9760 wcroot, repos_id, result_pool));
9761
9762 if (!base_tree_only && mtb->kind == svn_node_dir)
9763 SVN_ERR(is_wclocked(&mtb->locked, wcroot, local_relpath, scratch_pool));
9764
9765 if (mtb->kind == svn_node_dir)
9766 mtb->has_descendants = TRUE;
9767 else
9768 SVN_ERR(find_conflict_descendants(&mtb->has_descendants,
9769 wcroot, local_relpath, scratch_pool));
9770
9771 *info = mtb;
9772
9773 return SVN_NO_ERROR;
9774 }
9775
9776 svn_error_t *
svn_wc__db_read_single_info(const struct svn_wc__db_info_t ** info,svn_wc__db_t * db,const char * local_abspath,svn_boolean_t base_tree_only,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9777 svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info,
9778 svn_wc__db_t *db,
9779 const char *local_abspath,
9780 svn_boolean_t base_tree_only,
9781 apr_pool_t *result_pool,
9782 apr_pool_t *scratch_pool)
9783 {
9784 svn_wc__db_wcroot_t *wcroot;
9785 const char *local_relpath;
9786
9787 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9788
9789 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9790 local_abspath,
9791 scratch_pool, scratch_pool));
9792 VERIFY_USABLE_WCROOT(wcroot);
9793
9794 SVN_WC__DB_WITH_TXN(read_single_info(info, wcroot, local_relpath,
9795 base_tree_only,
9796 result_pool, scratch_pool),
9797 wcroot);
9798
9799 return SVN_NO_ERROR;
9800 }
9801
9802 svn_error_t *
svn_wc__db_read_pristine_info(svn_wc__db_status_t * status,svn_node_kind_t * kind,svn_revnum_t * changed_rev,apr_time_t * changed_date,const char ** changed_author,svn_depth_t * depth,const svn_checksum_t ** checksum,const char ** target,svn_boolean_t * had_props,apr_hash_t ** props,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9803 svn_wc__db_read_pristine_info(svn_wc__db_status_t *status,
9804 svn_node_kind_t *kind,
9805 svn_revnum_t *changed_rev,
9806 apr_time_t *changed_date,
9807 const char **changed_author,
9808 svn_depth_t *depth, /* dirs only */
9809 const svn_checksum_t **checksum, /* files only */
9810 const char **target, /* symlinks only */
9811 svn_boolean_t *had_props,
9812 apr_hash_t **props,
9813 svn_wc__db_t *db,
9814 const char *local_abspath,
9815 apr_pool_t *result_pool,
9816 apr_pool_t *scratch_pool)
9817 {
9818 svn_wc__db_wcroot_t *wcroot;
9819 const char *local_relpath;
9820 svn_sqlite__stmt_t *stmt;
9821 svn_boolean_t have_row;
9822 svn_error_t *err = NULL;
9823 int op_depth;
9824 svn_wc__db_status_t raw_status;
9825 svn_node_kind_t node_kind;
9826
9827 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9828
9829 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9830 local_abspath,
9831 scratch_pool, scratch_pool));
9832 VERIFY_USABLE_WCROOT(wcroot);
9833
9834 /* Obtain the most likely to exist record first, to make sure we don't
9835 have to obtain the SQLite read-lock multiple times */
9836 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9837 STMT_SELECT_NODE_INFO));
9838 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9839 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9840
9841 if (!have_row)
9842 {
9843 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9844 svn_sqlite__reset(stmt),
9845 _("The node '%s' was not found."),
9846 path_for_error_message(wcroot,
9847 local_relpath,
9848 scratch_pool));
9849 }
9850
9851 op_depth = svn_sqlite__column_int(stmt, 0);
9852 raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9853
9854 if (op_depth > 0 && raw_status == svn_wc__db_status_base_deleted)
9855 {
9856 SVN_ERR(svn_sqlite__step_row(stmt));
9857
9858 op_depth = svn_sqlite__column_int(stmt, 0);
9859 raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9860 }
9861
9862 node_kind = svn_sqlite__column_token(stmt, 4, kind_map);
9863
9864 if (status)
9865 {
9866 if (op_depth > 0)
9867 {
9868 err = svn_error_compose_create(err,
9869 convert_to_working_status(
9870 status,
9871 raw_status));
9872 }
9873 else
9874 *status = raw_status;
9875 }
9876 if (kind)
9877 {
9878 *kind = node_kind;
9879 }
9880 if (changed_rev)
9881 {
9882 *changed_rev = svn_sqlite__column_revnum(stmt, 8);
9883 }
9884 if (changed_date)
9885 {
9886 *changed_date = svn_sqlite__column_int64(stmt, 9);
9887 }
9888 if (changed_author)
9889 {
9890 *changed_author = svn_sqlite__column_text(stmt, 10,
9891 result_pool);
9892 }
9893 if (depth)
9894 {
9895 if (node_kind != svn_node_dir)
9896 {
9897 *depth = svn_depth_unknown;
9898 }
9899 else
9900 {
9901 *depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
9902 svn_depth_unknown);
9903 }
9904 }
9905 if (checksum)
9906 {
9907 if (node_kind != svn_node_file)
9908 {
9909 *checksum = NULL;
9910 }
9911 else
9912 {
9913 svn_error_t *err2;
9914 err2 = svn_sqlite__column_checksum(checksum, stmt, 6, result_pool);
9915
9916 if (err2 != NULL)
9917 {
9918 if (err)
9919 err = svn_error_compose_create(
9920 err,
9921 svn_error_createf(
9922 err->apr_err, err2,
9923 _("The node '%s' has a corrupt checksum value."),
9924 path_for_error_message(wcroot, local_relpath,
9925 scratch_pool)));
9926 else
9927 err = err2;
9928 }
9929 }
9930 }
9931 if (target)
9932 {
9933 if (node_kind != svn_node_symlink)
9934 *target = NULL;
9935 else
9936 *target = svn_sqlite__column_text(stmt, 12, result_pool);
9937 }
9938 if (had_props)
9939 {
9940 *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
9941 }
9942 if (props)
9943 {
9944 if (raw_status == svn_wc__db_status_normal
9945 || raw_status == svn_wc__db_status_incomplete)
9946 {
9947 SVN_ERR(svn_sqlite__column_properties(props, stmt, 14,
9948 result_pool, scratch_pool));
9949 if (*props == NULL)
9950 *props = apr_hash_make(result_pool);
9951 }
9952 else
9953 {
9954 assert(svn_sqlite__column_is_null(stmt, 14));
9955 *props = NULL;
9956 }
9957 }
9958
9959 return svn_error_trace(
9960 svn_error_compose_create(err,
9961 svn_sqlite__reset(stmt)));
9962 }
9963
9964 svn_error_t *
svn_wc__db_read_children_walker_info(const apr_array_header_t ** items,svn_wc__db_t * db,const char * dir_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)9965 svn_wc__db_read_children_walker_info(const apr_array_header_t **items,
9966 svn_wc__db_t *db,
9967 const char *dir_abspath,
9968 apr_pool_t *result_pool,
9969 apr_pool_t *scratch_pool)
9970 {
9971 svn_wc__db_wcroot_t *wcroot;
9972 const char *dir_relpath;
9973 svn_sqlite__stmt_t *stmt;
9974 svn_boolean_t have_row;
9975 apr_array_header_t *nodes;
9976
9977 SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9978
9979 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9980 dir_abspath,
9981 scratch_pool, scratch_pool));
9982 VERIFY_USABLE_WCROOT(wcroot);
9983
9984 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9985 STMT_SELECT_NODE_CHILDREN_WALKER_INFO));
9986 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9987 SVN_ERR(svn_sqlite__step(&have_row, stmt));
9988
9989 nodes = apr_array_make(result_pool, 16,
9990 sizeof(struct svn_wc__db_walker_info_t *));
9991 while (have_row)
9992 {
9993 struct svn_wc__db_walker_info_t *child;
9994 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9995 const char *name = svn_relpath_basename(child_relpath, result_pool);
9996 int op_depth = svn_sqlite__column_int(stmt, 1);
9997 svn_error_t *err;
9998
9999 child = apr_palloc(result_pool, sizeof(*child));
10000 child->name = name;
10001 child->status = svn_sqlite__column_token(stmt, 2, presence_map);
10002 if (op_depth > 0)
10003 {
10004 err = convert_to_working_status(&child->status, child->status);
10005 if (err)
10006 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10007 }
10008 child->kind = svn_sqlite__column_token(stmt, 3, kind_map);
10009
10010 APR_ARRAY_PUSH(nodes, struct svn_wc__db_walker_info_t *) = child;
10011
10012 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10013 }
10014
10015 SVN_ERR(svn_sqlite__reset(stmt));
10016
10017 *items = nodes;
10018
10019 return SVN_NO_ERROR;
10020 }
10021
10022 svn_error_t *
svn_wc__db_read_node_install_info(const char ** wcroot_abspath,const svn_checksum_t ** sha1_checksum,apr_hash_t ** pristine_props,apr_time_t * changed_date,svn_wc__db_t * db,const char * local_abspath,const char * wri_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10023 svn_wc__db_read_node_install_info(const char **wcroot_abspath,
10024 const svn_checksum_t **sha1_checksum,
10025 apr_hash_t **pristine_props,
10026 apr_time_t *changed_date,
10027 svn_wc__db_t *db,
10028 const char *local_abspath,
10029 const char *wri_abspath,
10030 apr_pool_t *result_pool,
10031 apr_pool_t *scratch_pool)
10032 {
10033 svn_wc__db_wcroot_t *wcroot;
10034 const char *local_relpath;
10035 svn_sqlite__stmt_t *stmt;
10036 svn_error_t *err = NULL;
10037 svn_boolean_t have_row;
10038
10039 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10040
10041 if (!wri_abspath)
10042 wri_abspath = local_abspath;
10043
10044 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10045 wri_abspath, scratch_pool, scratch_pool));
10046 VERIFY_USABLE_WCROOT(wcroot);
10047
10048 if (local_abspath != wri_abspath
10049 && strcmp(local_abspath, wri_abspath))
10050 {
10051 if (!svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
10052 return svn_error_createf(
10053 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
10054 _("The node '%s' is not in working copy '%s'"),
10055 svn_dirent_local_style(local_abspath, scratch_pool),
10056 svn_dirent_local_style(wcroot->abspath, scratch_pool));
10057
10058 local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
10059 }
10060
10061 if (wcroot_abspath != NULL)
10062 *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
10063
10064 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10065 STMT_SELECT_NODE_INFO));
10066
10067 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10068
10069 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10070
10071 if (have_row)
10072 {
10073 if (sha1_checksum)
10074 err = svn_sqlite__column_checksum(sha1_checksum, stmt, 6, result_pool);
10075
10076 if (!err && pristine_props)
10077 {
10078 err = svn_sqlite__column_properties(pristine_props, stmt, 14,
10079 result_pool, scratch_pool);
10080 /* Null means no props (assuming presence normal or incomplete). */
10081 if (*pristine_props == NULL)
10082 *pristine_props = apr_hash_make(result_pool);
10083 }
10084
10085 if (changed_date)
10086 *changed_date = svn_sqlite__column_int64(stmt, 9);
10087 }
10088 else
10089 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10090 svn_sqlite__reset(stmt),
10091 _("The node '%s' is not installable"),
10092 svn_dirent_local_style(local_abspath,
10093 scratch_pool));
10094
10095 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10096
10097 return SVN_NO_ERROR;
10098 }
10099
10100
10101
10102 /* The body of svn_wc__db_read_repos_info().
10103 */
10104 static svn_error_t *
db_read_repos_info(svn_revnum_t * revision,const char ** repos_relpath,apr_int64_t * repos_id,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10105 db_read_repos_info(svn_revnum_t *revision,
10106 const char **repos_relpath,
10107 apr_int64_t *repos_id,
10108 svn_wc__db_wcroot_t *wcroot,
10109 const char *local_relpath,
10110 apr_pool_t *result_pool,
10111 apr_pool_t *scratch_pool)
10112 {
10113 svn_wc__db_status_t status;
10114
10115 SVN_ERR(read_info(&status, NULL, revision, repos_relpath, repos_id, NULL,
10116 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10117 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10118 NULL, NULL, NULL,
10119 wcroot, local_relpath, result_pool, scratch_pool));
10120
10121 if ((repos_relpath && !*repos_relpath)
10122 || (repos_id && *repos_id == INVALID_REPOS_ID))
10123 {
10124 if (status == svn_wc__db_status_added)
10125 {
10126 SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL,
10127 NULL, NULL, NULL, NULL, NULL,
10128 wcroot, local_relpath,
10129 result_pool, scratch_pool));
10130 }
10131 else if (status == svn_wc__db_status_deleted)
10132 {
10133 const char *base_del_relpath;
10134 const char *work_del_relpath;
10135
10136 SVN_ERR(scan_deletion(&base_del_relpath, NULL,
10137 &work_del_relpath,
10138 NULL, wcroot,
10139 local_relpath,
10140 scratch_pool,
10141 scratch_pool));
10142
10143 if (work_del_relpath)
10144 {
10145 /* The parent of the WORKING delete, must be an addition */
10146 const char *work_relpath = NULL;
10147
10148 /* work_del_relpath should not be NULL. However, we have
10149 * observed instances where that assumption was not met.
10150 * Bail out in that case instead of crashing with a segfault.
10151 */
10152 SVN_ERR_ASSERT(work_del_relpath != NULL);
10153 work_relpath = svn_relpath_dirname(work_del_relpath,
10154 scratch_pool);
10155
10156 SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id,
10157 NULL, NULL, NULL, NULL, NULL, NULL,
10158 wcroot, work_relpath,
10159 scratch_pool, scratch_pool));
10160
10161 if (repos_relpath)
10162 *repos_relpath = svn_relpath_join(
10163 *repos_relpath,
10164 svn_dirent_skip_ancestor(work_relpath,
10165 local_relpath),
10166 result_pool);
10167 }
10168 else if (base_del_relpath)
10169 {
10170 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, revision,
10171 repos_relpath,
10172 repos_id,
10173 NULL, NULL, NULL,
10174 NULL, NULL, NULL,
10175 NULL, NULL, NULL, NULL,
10176 wcroot,
10177 base_del_relpath,
10178 scratch_pool,
10179 scratch_pool));
10180
10181 if (repos_relpath)
10182 *repos_relpath = svn_relpath_join(
10183 *repos_relpath,
10184 svn_dirent_skip_ancestor(base_del_relpath,
10185 local_relpath),
10186 result_pool);
10187 }
10188 else
10189 SVN_ERR_MALFUNCTION();
10190 }
10191 else if (status == svn_wc__db_status_excluded)
10192 {
10193 const char *parent_relpath;
10194 const char *name;
10195
10196 /* A BASE excluded would have had repository information, so
10197 we have a working exclude, which must be below an addition */
10198
10199 svn_relpath_split(&parent_relpath, &name, local_relpath,
10200 scratch_pool);
10201 SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL,
10202 NULL, NULL, NULL, NULL, NULL,
10203 wcroot, parent_relpath,
10204 scratch_pool, scratch_pool));
10205
10206 if (repos_relpath)
10207 *repos_relpath = svn_relpath_join(*repos_relpath, name,
10208 result_pool);
10209
10210 return SVN_NO_ERROR;
10211 }
10212 else
10213 {
10214 /* All working statee are explicitly handled and all base statee
10215 have a repos_relpath */
10216 SVN_ERR_MALFUNCTION();
10217 }
10218 }
10219
10220 return SVN_NO_ERROR;
10221 }
10222
10223
10224 svn_error_t *
svn_wc__db_read_repos_info(svn_revnum_t * revision,const char ** repos_relpath,const char ** repos_root_url,const char ** repos_uuid,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10225 svn_wc__db_read_repos_info(svn_revnum_t *revision,
10226 const char **repos_relpath,
10227 const char **repos_root_url,
10228 const char **repos_uuid,
10229 svn_wc__db_t *db,
10230 const char *local_abspath,
10231 apr_pool_t *result_pool,
10232 apr_pool_t *scratch_pool)
10233 {
10234 svn_wc__db_wcroot_t *wcroot;
10235 const char *local_relpath;
10236 apr_int64_t repos_id = INVALID_REPOS_ID;
10237
10238 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10239
10240 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10241 local_abspath,
10242 scratch_pool, scratch_pool));
10243 VERIFY_USABLE_WCROOT(wcroot);
10244
10245 SVN_WC__DB_WITH_TXN4(db_read_repos_info(revision, repos_relpath,
10246 (repos_root_url || repos_uuid)
10247 ? &repos_id : NULL,
10248 wcroot, local_relpath,
10249 result_pool, scratch_pool),
10250 svn_wc__db_fetch_repos_info(repos_root_url,
10251 repos_uuid,
10252 wcroot, repos_id,
10253 result_pool),
10254 SVN_NO_ERROR, SVN_NO_ERROR,
10255 wcroot);
10256
10257 return SVN_NO_ERROR;
10258 }
10259
10260
10261 /* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
10262 a hash table mapping <tt>char *</tt> names onto svn_string_t *
10263 values for any properties of immediate or recursive child nodes of
10264 LOCAL_ABSPATH, the actual query being determined by STMT_IDX.
10265 If FILES_ONLY is true, only report properties for file child nodes.
10266 Check for cancellation between calls of RECEIVER_FUNC.
10267 */
10268 typedef struct cache_props_baton_t
10269 {
10270 svn_depth_t depth;
10271 svn_boolean_t pristine;
10272 const apr_array_header_t *changelists;
10273 svn_cancel_func_t cancel_func;
10274 void *cancel_baton;
10275 } cache_props_baton_t;
10276
10277
10278 static svn_error_t *
cache_props_recursive(void * cb_baton,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)10279 cache_props_recursive(void *cb_baton,
10280 svn_wc__db_wcroot_t *wcroot,
10281 const char *local_relpath,
10282 apr_pool_t *scratch_pool)
10283 {
10284 cache_props_baton_t *baton = cb_baton;
10285 svn_sqlite__stmt_t *stmt;
10286 int stmt_idx;
10287
10288 SVN_ERR(populate_targets_tree(wcroot, local_relpath, baton->depth,
10289 baton->changelists, scratch_pool));
10290
10291 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
10292 STMT_CREATE_TARGET_PROP_CACHE));
10293
10294 if (baton->pristine)
10295 stmt_idx = STMT_CACHE_TARGET_PRISTINE_PROPS;
10296 else
10297 stmt_idx = STMT_CACHE_TARGET_PROPS;
10298
10299 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
10300 SVN_ERR(svn_sqlite__bind_int64(stmt, 1, wcroot->wc_id));
10301 SVN_ERR(svn_sqlite__step_done(stmt));
10302
10303 return SVN_NO_ERROR;
10304 }
10305
10306
10307 svn_error_t *
svn_wc__db_read_props_streamily(svn_wc__db_t * db,const char * local_abspath,svn_depth_t depth,svn_boolean_t pristine,const apr_array_header_t * changelists,svn_wc__proplist_receiver_t receiver_func,void * receiver_baton,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * scratch_pool)10308 svn_wc__db_read_props_streamily(svn_wc__db_t *db,
10309 const char *local_abspath,
10310 svn_depth_t depth,
10311 svn_boolean_t pristine,
10312 const apr_array_header_t *changelists,
10313 svn_wc__proplist_receiver_t receiver_func,
10314 void *receiver_baton,
10315 svn_cancel_func_t cancel_func,
10316 void *cancel_baton,
10317 apr_pool_t *scratch_pool)
10318 {
10319 svn_wc__db_wcroot_t *wcroot;
10320 const char *local_relpath;
10321 svn_sqlite__stmt_t *stmt;
10322 cache_props_baton_t baton;
10323 svn_boolean_t have_row;
10324 apr_pool_t *iterpool;
10325 svn_error_t *err = NULL;
10326
10327 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10328 SVN_ERR_ASSERT(receiver_func);
10329 SVN_ERR_ASSERT((depth == svn_depth_files) ||
10330 (depth == svn_depth_immediates) ||
10331 (depth == svn_depth_infinity));
10332
10333 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10334 db, local_abspath,
10335 scratch_pool, scratch_pool));
10336 VERIFY_USABLE_WCROOT(wcroot);
10337
10338 baton.depth = depth;
10339 baton.pristine = pristine;
10340 baton.changelists = changelists;
10341 baton.cancel_func = cancel_func;
10342 baton.cancel_baton = cancel_baton;
10343
10344 SVN_ERR(with_finalization(wcroot, local_relpath,
10345 cache_props_recursive, &baton,
10346 NULL, NULL,
10347 cancel_func, cancel_baton,
10348 NULL, NULL,
10349 STMT_DROP_TARGETS_LIST,
10350 scratch_pool));
10351
10352 iterpool = svn_pool_create(scratch_pool);
10353
10354 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10355 STMT_SELECT_ALL_TARGET_PROP_CACHE));
10356 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10357 while (!err && have_row)
10358 {
10359 apr_hash_t *props;
10360
10361 svn_pool_clear(iterpool);
10362
10363 SVN_ERR(svn_sqlite__column_properties(&props, stmt, 1, iterpool,
10364 iterpool));
10365
10366 /* see if someone wants to cancel this operation. */
10367 if (cancel_func)
10368 err = cancel_func(cancel_baton);
10369
10370 if (!err && props && apr_hash_count(props) != 0)
10371 {
10372 const char *child_relpath;
10373 const char *child_abspath;
10374
10375 child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
10376 child_abspath = svn_dirent_join(wcroot->abspath,
10377 child_relpath, iterpool);
10378
10379 err = receiver_func(receiver_baton, child_abspath, props, iterpool);
10380 }
10381
10382 err = svn_error_compose_create(err, svn_sqlite__step(&have_row, stmt));
10383 }
10384
10385 err = svn_error_compose_create(err, svn_sqlite__reset(stmt));
10386
10387 svn_pool_destroy(iterpool);
10388
10389 SVN_ERR(svn_error_compose_create(
10390 err,
10391 svn_sqlite__exec_statements(wcroot->sdb,
10392 STMT_DROP_TARGET_PROP_CACHE)));
10393 return SVN_NO_ERROR;
10394 }
10395
10396
10397 /* Helper for svn_wc__db_read_props().
10398 */
10399 svn_error_t *
svn_wc__db_read_props_internal(apr_hash_t ** props,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10400 svn_wc__db_read_props_internal(apr_hash_t **props,
10401 svn_wc__db_wcroot_t *wcroot,
10402 const char *local_relpath,
10403 apr_pool_t *result_pool,
10404 apr_pool_t *scratch_pool)
10405 {
10406 svn_sqlite__stmt_t *stmt;
10407 svn_boolean_t have_row;
10408 svn_error_t *err = NULL;
10409
10410 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10411 STMT_SELECT_ACTUAL_PROPS));
10412 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10413 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10414
10415 if (have_row && !svn_sqlite__column_is_null(stmt, 0))
10416 {
10417 err = svn_sqlite__column_properties(props, stmt, 0,
10418 result_pool, scratch_pool);
10419 }
10420 else
10421 have_row = FALSE;
10422
10423 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10424
10425 if (have_row)
10426 return SVN_NO_ERROR;
10427
10428 /* No local changes. Return the pristine props for this node. */
10429 SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, FALSE,
10430 result_pool, scratch_pool));
10431 if (*props == NULL)
10432 {
10433 /* Pristine properties are not defined for this node.
10434 ### we need to determine whether this node is in a state that
10435 ### allows for ACTUAL properties (ie. not deleted). for now,
10436 ### just say all nodes, no matter the state, have at least an
10437 ### empty set of props. */
10438 *props = apr_hash_make(result_pool);
10439 }
10440
10441 return SVN_NO_ERROR;
10442 }
10443
10444
10445 svn_error_t *
svn_wc__db_read_props(apr_hash_t ** props,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10446 svn_wc__db_read_props(apr_hash_t **props,
10447 svn_wc__db_t *db,
10448 const char *local_abspath,
10449 apr_pool_t *result_pool,
10450 apr_pool_t *scratch_pool)
10451 {
10452 svn_wc__db_wcroot_t *wcroot;
10453 const char *local_relpath;
10454
10455 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10456
10457 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10458 local_abspath, scratch_pool, scratch_pool));
10459 VERIFY_USABLE_WCROOT(wcroot);
10460
10461 SVN_WC__DB_WITH_TXN(svn_wc__db_read_props_internal(props, wcroot,
10462 local_relpath,
10463 result_pool,
10464 scratch_pool),
10465 wcroot);
10466
10467 return SVN_NO_ERROR;
10468 }
10469
10470
10471 static svn_error_t *
db_read_pristine_props(apr_hash_t ** props,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_boolean_t deleted_ok,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10472 db_read_pristine_props(apr_hash_t **props,
10473 svn_wc__db_wcroot_t *wcroot,
10474 const char *local_relpath,
10475 svn_boolean_t deleted_ok,
10476 apr_pool_t *result_pool,
10477 apr_pool_t *scratch_pool)
10478 {
10479 svn_sqlite__stmt_t *stmt;
10480 svn_boolean_t have_row;
10481 svn_wc__db_status_t presence;
10482
10483 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_NODE_PROPS));
10484 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10485
10486 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10487
10488 if (!have_row)
10489 {
10490 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10491 svn_sqlite__reset(stmt),
10492 _("The node '%s' was not found."),
10493 path_for_error_message(wcroot,
10494 local_relpath,
10495 scratch_pool));
10496 }
10497
10498
10499 /* Examine the presence: */
10500 presence = svn_sqlite__column_token(stmt, 1, presence_map);
10501
10502 /* For "base-deleted", it is obvious the pristine props are located
10503 below the current node. Fetch the NODE from the next record. */
10504 if (presence == svn_wc__db_status_base_deleted && deleted_ok)
10505 {
10506 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10507
10508 SVN_ERR_ASSERT(have_row);
10509
10510 presence = svn_sqlite__column_token(stmt, 1, presence_map);
10511 }
10512
10513 /* normal or copied: Fetch properties (during update we want
10514 properties for incomplete as well) */
10515 if (presence == svn_wc__db_status_normal
10516 || presence == svn_wc__db_status_incomplete)
10517 {
10518 svn_error_t *err;
10519
10520 err = svn_sqlite__column_properties(props, stmt, 0, result_pool,
10521 scratch_pool);
10522 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10523
10524 if (!*props)
10525 *props = apr_hash_make(result_pool);
10526
10527 return SVN_NO_ERROR;
10528 }
10529
10530 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
10531 svn_sqlite__reset(stmt),
10532 _("The node '%s' has a status that"
10533 " has no properties."),
10534 path_for_error_message(wcroot,
10535 local_relpath,
10536 scratch_pool));
10537 }
10538
10539
10540 svn_error_t *
svn_wc__db_read_pristine_props(apr_hash_t ** props,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10541 svn_wc__db_read_pristine_props(apr_hash_t **props,
10542 svn_wc__db_t *db,
10543 const char *local_abspath,
10544 apr_pool_t *result_pool,
10545 apr_pool_t *scratch_pool)
10546 {
10547 svn_wc__db_wcroot_t *wcroot;
10548 const char *local_relpath;
10549
10550 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10551
10552 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10553 local_abspath, scratch_pool, scratch_pool));
10554 VERIFY_USABLE_WCROOT(wcroot);
10555
10556 SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, TRUE,
10557 result_pool, scratch_pool));
10558 return SVN_NO_ERROR;
10559 }
10560
10561 svn_error_t *
svn_wc__db_prop_retrieve_recursive(apr_hash_t ** values,svn_wc__db_t * db,const char * local_abspath,const char * propname,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10562 svn_wc__db_prop_retrieve_recursive(apr_hash_t **values,
10563 svn_wc__db_t *db,
10564 const char *local_abspath,
10565 const char *propname,
10566 apr_pool_t *result_pool,
10567 apr_pool_t *scratch_pool)
10568 {
10569 svn_wc__db_wcroot_t *wcroot;
10570 const char *local_relpath;
10571 svn_sqlite__stmt_t *stmt;
10572 svn_boolean_t have_row;
10573 apr_pool_t *iterpool;
10574
10575 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10576
10577 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10578 local_abspath, scratch_pool, scratch_pool));
10579 VERIFY_USABLE_WCROOT(wcroot);
10580
10581 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10582 STMT_SELECT_CURRENT_PROPS_RECURSIVE));
10583 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10584
10585 *values = apr_hash_make(result_pool);
10586
10587 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10588 iterpool = svn_pool_create(scratch_pool);
10589 while (have_row)
10590 {
10591 apr_hash_t *node_props;
10592 svn_string_t *value;
10593
10594 svn_pool_clear(iterpool);
10595
10596 SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 0,
10597 iterpool, iterpool));
10598
10599 value = (node_props
10600 ? svn_hash_gets(node_props, propname)
10601 : NULL);
10602
10603 if (value)
10604 {
10605 svn_hash_sets(*values,
10606 svn_dirent_join(wcroot->abspath,
10607 svn_sqlite__column_text(stmt, 1, NULL),
10608 result_pool),
10609 svn_string_dup(value, result_pool));
10610 }
10611
10612 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10613 }
10614
10615 svn_pool_destroy(iterpool);
10616
10617 return svn_error_trace(svn_sqlite__reset(stmt));
10618 }
10619
10620 /* Remove all prop name value pairs from PROP_HASH where the property
10621 name is not PROPNAME. */
10622 static void
filter_unwanted_props(apr_hash_t * prop_hash,const char * propname,apr_pool_t * scratch_pool)10623 filter_unwanted_props(apr_hash_t *prop_hash,
10624 const char * propname,
10625 apr_pool_t *scratch_pool)
10626 {
10627 apr_hash_index_t *hi;
10628
10629 for (hi = apr_hash_first(scratch_pool, prop_hash);
10630 hi;
10631 hi = apr_hash_next(hi))
10632 {
10633 const char *ipropname = apr_hash_this_key(hi);
10634
10635 if (strcmp(ipropname, propname) != 0)
10636 svn_hash_sets(prop_hash, ipropname, NULL);
10637 }
10638 return;
10639 }
10640
10641 /* Get the changed properties as stored in the ACTUAL table */
10642 static svn_error_t *
db_get_changed_props(apr_hash_t ** actual_props,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10643 db_get_changed_props(apr_hash_t **actual_props,
10644 svn_wc__db_wcroot_t *wcroot,
10645 const char *local_relpath,
10646 apr_pool_t *result_pool,
10647 apr_pool_t *scratch_pool)
10648 {
10649 svn_sqlite__stmt_t *stmt;
10650 svn_boolean_t have_row;
10651 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10652 STMT_SELECT_ACTUAL_PROPS));
10653 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10654 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10655
10656 if (have_row && !svn_sqlite__column_is_null(stmt, 0))
10657 SVN_ERR(svn_sqlite__column_properties(actual_props, stmt, 0,
10658 result_pool, scratch_pool));
10659 else
10660 *actual_props = NULL; /* Cached when we read that record */
10661
10662 return svn_error_trace(svn_sqlite__reset(stmt));
10663 }
10664
10665 /* The body of svn_wc__db_read_inherited_props(). */
10666 static svn_error_t *
db_read_inherited_props(apr_array_header_t ** inherited_props,apr_hash_t ** actual_props,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,const char * propname,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10667 db_read_inherited_props(apr_array_header_t **inherited_props,
10668 apr_hash_t **actual_props,
10669 svn_wc__db_wcroot_t *wcroot,
10670 const char *local_relpath,
10671 const char *propname,
10672 apr_pool_t *result_pool,
10673 apr_pool_t *scratch_pool)
10674 {
10675 int i;
10676 apr_array_header_t *cached_iprops = NULL;
10677 apr_array_header_t *iprops;
10678 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
10679 svn_sqlite__stmt_t *stmt;
10680 const char *relpath;
10681 const char *expected_parent_repos_relpath = NULL;
10682 const char *parent_relpath;
10683
10684 iprops = apr_array_make(result_pool, 1,
10685 sizeof(svn_prop_inherited_item_t *));
10686 *inherited_props = iprops;
10687
10688 if (actual_props)
10689 *actual_props = NULL;
10690
10691 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10692 STMT_SELECT_NODE_INFO));
10693
10694 relpath = local_relpath;
10695
10696 /* Walk up to the root of the WC looking for inherited properties. When we
10697 reach the WC root also check for cached inherited properties. */
10698 for (relpath = local_relpath; relpath; relpath = parent_relpath)
10699 {
10700 svn_boolean_t have_row;
10701 int op_depth;
10702 svn_wc__db_status_t status;
10703 apr_hash_t *node_props;
10704
10705 parent_relpath = relpath[0] ? svn_relpath_dirname(relpath, scratch_pool)
10706 : NULL;
10707
10708 svn_pool_clear(iterpool);
10709
10710 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, relpath));
10711
10712 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10713
10714 if (!have_row)
10715 return svn_error_createf(
10716 SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
10717 _("The node '%s' was not found."),
10718 path_for_error_message(wcroot, relpath,
10719 scratch_pool));
10720
10721 op_depth = svn_sqlite__column_int(stmt, 0);
10722
10723 status = svn_sqlite__column_token(stmt, 3, presence_map);
10724
10725 if (status != svn_wc__db_status_normal
10726 && status != svn_wc__db_status_incomplete)
10727 return svn_error_createf(
10728 SVN_ERR_WC_PATH_UNEXPECTED_STATUS, svn_sqlite__reset(stmt),
10729 _("The node '%s' has a status that has no properties."),
10730 path_for_error_message(wcroot, relpath,
10731 scratch_pool));
10732
10733 if (op_depth > 0)
10734 {
10735 /* WORKING node. Nothing to check */
10736 }
10737 else if (expected_parent_repos_relpath)
10738 {
10739 const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10740
10741 if (strcmp(expected_parent_repos_relpath, repos_relpath) != 0)
10742 {
10743 /* The child of this node has a different parent than this node
10744 (It is "switched"), so we can stop here. Note that switched
10745 with the same parent is not interesting for us here. */
10746 SVN_ERR(svn_sqlite__reset(stmt));
10747 break;
10748 }
10749
10750 expected_parent_repos_relpath =
10751 svn_relpath_dirname(expected_parent_repos_relpath, scratch_pool);
10752 }
10753 else
10754 {
10755 const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10756
10757 expected_parent_repos_relpath =
10758 svn_relpath_dirname(repos_relpath, scratch_pool);
10759 }
10760
10761 if (op_depth == 0
10762 && !svn_sqlite__column_is_null(stmt, 16))
10763 {
10764 /* The node contains a cache. No reason to look further */
10765 SVN_ERR(svn_sqlite__column_iprops(&cached_iprops, stmt, 16,
10766 result_pool, iterpool));
10767
10768 parent_relpath = NULL; /* Stop after this */
10769 }
10770
10771 SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 14,
10772 iterpool, iterpool));
10773
10774 SVN_ERR(svn_sqlite__reset(stmt));
10775
10776 /* If PARENT_ABSPATH is a parent of LOCAL_ABSPATH, then LOCAL_ABSPATH
10777 can inherit properties from it. */
10778 if (relpath != local_relpath)
10779 {
10780 apr_hash_t *changed_props;
10781
10782 SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10783 result_pool, iterpool));
10784
10785 if (changed_props)
10786 node_props = changed_props;
10787 else if (node_props)
10788 node_props = svn_prop_hash_dup(node_props, result_pool);
10789
10790 if (node_props && apr_hash_count(node_props))
10791 {
10792 /* If we only want PROPNAME filter out any other properties. */
10793 if (propname)
10794 filter_unwanted_props(node_props, propname, iterpool);
10795
10796 if (apr_hash_count(node_props))
10797 {
10798 svn_prop_inherited_item_t *iprop_elt =
10799 apr_pcalloc(result_pool,
10800 sizeof(svn_prop_inherited_item_t));
10801 iprop_elt->path_or_url = svn_dirent_join(wcroot->abspath,
10802 relpath,
10803 result_pool);
10804
10805 iprop_elt->prop_hash = node_props;
10806 /* Build the output array in depth-first order. */
10807 SVN_ERR(svn_sort__array_insert2(iprops, &iprop_elt, 0));
10808 }
10809 }
10810 }
10811 else if (actual_props)
10812 {
10813 apr_hash_t *changed_props;
10814
10815 SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10816 result_pool, iterpool));
10817
10818 if (changed_props)
10819 *actual_props = changed_props;
10820 else if (node_props)
10821 *actual_props = svn_prop_hash_dup(node_props, result_pool);
10822 }
10823 }
10824
10825 if (cached_iprops)
10826 {
10827 for (i = cached_iprops->nelts - 1; i >= 0; i--)
10828 {
10829 svn_prop_inherited_item_t *cached_iprop =
10830 APR_ARRAY_IDX(cached_iprops, i, svn_prop_inherited_item_t *);
10831
10832 /* An empty property hash in the iprops cache means there are no
10833 inherited properties. */
10834 if (apr_hash_count(cached_iprop->prop_hash) == 0)
10835 continue;
10836
10837 if (propname)
10838 filter_unwanted_props(cached_iprop->prop_hash, propname,
10839 scratch_pool);
10840
10841 /* If we didn't filter everything then keep this iprop. */
10842 if (apr_hash_count(cached_iprop->prop_hash))
10843 SVN_ERR(svn_sort__array_insert2(iprops, &cached_iprop, 0));
10844 }
10845 }
10846
10847 if (actual_props && !*actual_props)
10848 *actual_props = apr_hash_make(result_pool);
10849
10850 svn_pool_destroy(iterpool);
10851 return SVN_NO_ERROR;
10852 }
10853
10854 svn_error_t *
svn_wc__db_read_inherited_props(apr_array_header_t ** iprops,apr_hash_t ** actual_props,svn_wc__db_t * db,const char * local_abspath,const char * propname,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10855 svn_wc__db_read_inherited_props(apr_array_header_t **iprops,
10856 apr_hash_t **actual_props,
10857 svn_wc__db_t *db,
10858 const char *local_abspath,
10859 const char *propname,
10860 apr_pool_t *result_pool,
10861 apr_pool_t *scratch_pool)
10862 {
10863 svn_wc__db_wcroot_t *wcroot;
10864 const char *local_relpath;
10865
10866 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10867
10868 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10869 db, local_abspath,
10870 scratch_pool, scratch_pool));
10871 VERIFY_USABLE_WCROOT(wcroot);
10872
10873 SVN_WC__DB_WITH_TXN(db_read_inherited_props(iprops, actual_props,
10874 wcroot, local_relpath, propname,
10875 result_pool, scratch_pool),
10876 wcroot);
10877
10878 return SVN_NO_ERROR;
10879 }
10880
10881 /* The body of svn_wc__db_get_children_with_cached_iprops().
10882 */
10883 static svn_error_t *
get_children_with_cached_iprops(apr_hash_t ** iprop_paths,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_depth_t depth,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10884 get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10885 svn_wc__db_wcroot_t *wcroot,
10886 const char *local_relpath,
10887 svn_depth_t depth,
10888 apr_pool_t *result_pool,
10889 apr_pool_t *scratch_pool)
10890 {
10891 svn_sqlite__stmt_t *stmt;
10892 svn_boolean_t have_row;
10893
10894 *iprop_paths = apr_hash_make(result_pool);
10895
10896 /* First check if LOCAL_RELPATH itself has iprops */
10897 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10898 STMT_SELECT_IPROPS_NODE));
10899 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10900 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10901
10902 if (have_row)
10903 {
10904 const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10905 NULL);
10906 const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10907 relpath_with_cache,
10908 result_pool);
10909 svn_hash_sets(*iprop_paths, abspath_with_cache,
10910 svn_sqlite__column_text(stmt, 1, result_pool));
10911 }
10912 SVN_ERR(svn_sqlite__reset(stmt));
10913
10914 if (depth == svn_depth_empty)
10915 return SVN_NO_ERROR;
10916
10917 /* Now fetch information for children or all descendants */
10918 if (depth == svn_depth_files
10919 || depth == svn_depth_immediates)
10920 {
10921 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10922 STMT_SELECT_IPROPS_CHILDREN));
10923 }
10924 else /* Default to svn_depth_infinity. */
10925 {
10926 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10927 STMT_SELECT_IPROPS_RECURSIVE));
10928 }
10929
10930 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10931 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10932
10933 while (have_row)
10934 {
10935 const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10936 NULL);
10937 const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10938 relpath_with_cache,
10939 result_pool);
10940 svn_hash_sets(*iprop_paths, abspath_with_cache,
10941 svn_sqlite__column_text(stmt, 1, result_pool));
10942 SVN_ERR(svn_sqlite__step(&have_row, stmt));
10943 }
10944
10945 SVN_ERR(svn_sqlite__reset(stmt));
10946
10947 /* For depth files we should filter non files */
10948 if (depth == svn_depth_files)
10949 {
10950 apr_hash_index_t *hi;
10951 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
10952
10953 for (hi = apr_hash_first(scratch_pool, *iprop_paths);
10954 hi;
10955 hi = apr_hash_next(hi))
10956 {
10957 const char *child_abspath = apr_hash_this_key(hi);
10958 const char *child_relpath;
10959 svn_node_kind_t child_kind;
10960
10961 svn_pool_clear(iterpool);
10962
10963 child_relpath = svn_dirent_is_child(local_relpath, child_abspath,
10964 NULL);
10965
10966 if (! child_relpath)
10967 {
10968 continue; /* local_relpath itself */
10969 }
10970
10971 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &child_kind, NULL,
10972 NULL, NULL, NULL, NULL,
10973 NULL, NULL, NULL, NULL,
10974 NULL, NULL, NULL, NULL,
10975 wcroot, child_relpath,
10976 scratch_pool,
10977 scratch_pool));
10978
10979 /* Filter if not a file */
10980 if (child_kind != svn_node_file)
10981 {
10982 svn_hash_sets(*iprop_paths, child_abspath, NULL);
10983 }
10984 }
10985
10986 svn_pool_destroy(iterpool);
10987 }
10988
10989 return SVN_NO_ERROR;
10990 }
10991
10992 svn_error_t *
svn_wc__db_get_children_with_cached_iprops(apr_hash_t ** iprop_paths,svn_depth_t depth,const char * local_abspath,svn_wc__db_t * db,apr_pool_t * result_pool,apr_pool_t * scratch_pool)10993 svn_wc__db_get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10994 svn_depth_t depth,
10995 const char *local_abspath,
10996 svn_wc__db_t *db,
10997 apr_pool_t *result_pool,
10998 apr_pool_t *scratch_pool)
10999 {
11000 svn_wc__db_wcroot_t *wcroot;
11001 const char *local_relpath;
11002
11003 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11004
11005 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11006 local_abspath, scratch_pool,
11007 scratch_pool));
11008 VERIFY_USABLE_WCROOT(wcroot);
11009
11010 SVN_WC__DB_WITH_TXN(
11011 get_children_with_cached_iprops(iprop_paths, wcroot, local_relpath,
11012 depth, result_pool, scratch_pool),
11013 wcroot);
11014
11015 return SVN_NO_ERROR;
11016 }
11017
11018 svn_error_t *
svn_wc__db_read_children_of_working_node(const apr_array_header_t ** children,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11019 svn_wc__db_read_children_of_working_node(const apr_array_header_t **children,
11020 svn_wc__db_t *db,
11021 const char *local_abspath,
11022 apr_pool_t *result_pool,
11023 apr_pool_t *scratch_pool)
11024 {
11025 svn_wc__db_wcroot_t *wcroot;
11026 const char *local_relpath;
11027
11028 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11029
11030 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11031 local_abspath,
11032 scratch_pool, scratch_pool));
11033 VERIFY_USABLE_WCROOT(wcroot);
11034
11035 return svn_error_trace(
11036 gather_children(children, wcroot, local_relpath,
11037 STMT_SELECT_WORKING_CHILDREN, -1,
11038 result_pool, scratch_pool));
11039 }
11040
11041 svn_error_t *
svn_wc__db_base_read_not_present_children(const apr_array_header_t ** children,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11042 svn_wc__db_base_read_not_present_children(
11043 const apr_array_header_t **children,
11044 svn_wc__db_t *db,
11045 const char *local_abspath,
11046 apr_pool_t *result_pool,
11047 apr_pool_t *scratch_pool)
11048 {
11049 svn_wc__db_wcroot_t *wcroot;
11050 const char *local_relpath;
11051
11052 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11053
11054 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11055 local_abspath,
11056 scratch_pool, scratch_pool));
11057 VERIFY_USABLE_WCROOT(wcroot);
11058
11059 return svn_error_trace(
11060 gather_children(children, wcroot, local_relpath,
11061 STMT_SELECT_BASE_NOT_PRESENT_CHILDREN, -1,
11062 result_pool, scratch_pool));
11063 }
11064
11065 /* Helper for svn_wc__db_node_check_replace().
11066 */
11067 static svn_error_t *
check_replace_txn(svn_boolean_t * is_replace_root_p,svn_boolean_t * base_replace_p,svn_boolean_t * is_replace_p,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)11068 check_replace_txn(svn_boolean_t *is_replace_root_p,
11069 svn_boolean_t *base_replace_p,
11070 svn_boolean_t *is_replace_p,
11071 svn_wc__db_wcroot_t *wcroot,
11072 const char *local_relpath,
11073 apr_pool_t *scratch_pool)
11074 {
11075 svn_sqlite__stmt_t *stmt;
11076 svn_boolean_t have_row;
11077 svn_boolean_t is_replace = FALSE;
11078 int replaced_op_depth;
11079 svn_wc__db_status_t replaced_status;
11080
11081 /* Our caller initialized the output values to FALSE */
11082
11083 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11084 STMT_SELECT_NODE_INFO));
11085
11086 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11087
11088 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11089
11090 if (!have_row)
11091 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
11092 svn_sqlite__reset(stmt),
11093 _("The node '%s' was not found."),
11094 path_for_error_message(wcroot, local_relpath,
11095 scratch_pool));
11096
11097 {
11098 svn_wc__db_status_t status;
11099
11100 status = svn_sqlite__column_token(stmt, 3, presence_map);
11101
11102 if (status != svn_wc__db_status_normal)
11103 return svn_error_trace(svn_sqlite__reset(stmt));
11104 }
11105
11106 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11107
11108 if (!have_row)
11109 return svn_error_trace(svn_sqlite__reset(stmt));
11110
11111 replaced_status = svn_sqlite__column_token(stmt, 3, presence_map);
11112
11113 /* If the layer below the add describes a not present or a deleted node,
11114 this is not a replacement. Deleted can only occur if an ancestor is
11115 the delete root. */
11116 if (replaced_status != svn_wc__db_status_not_present
11117 && replaced_status != svn_wc__db_status_excluded
11118 && replaced_status != svn_wc__db_status_server_excluded
11119 && replaced_status != svn_wc__db_status_base_deleted)
11120 {
11121 is_replace = TRUE;
11122 if (is_replace_p)
11123 *is_replace_p = TRUE;
11124 }
11125
11126 replaced_op_depth = svn_sqlite__column_int(stmt, 0);
11127
11128 if (base_replace_p)
11129 {
11130 int op_depth = svn_sqlite__column_int(stmt, 0);
11131
11132 while (op_depth != 0 && have_row)
11133 {
11134 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11135
11136 if (have_row)
11137 op_depth = svn_sqlite__column_int(stmt, 0);
11138 }
11139
11140 if (have_row && op_depth == 0)
11141 {
11142 svn_wc__db_status_t base_status;
11143
11144 base_status = svn_sqlite__column_token(stmt, 3, presence_map);
11145
11146 *base_replace_p = (base_status != svn_wc__db_status_not_present);
11147 }
11148 }
11149
11150 SVN_ERR(svn_sqlite__reset(stmt));
11151
11152 if (!is_replace_root_p || !is_replace)
11153 return SVN_NO_ERROR;
11154
11155 if (replaced_status != svn_wc__db_status_base_deleted)
11156 {
11157 int parent_op_depth;
11158
11159 /* Check the current op-depth of the parent to see if we are a replacement
11160 root */
11161 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
11162 svn_relpath_dirname(local_relpath,
11163 scratch_pool)));
11164
11165 SVN_ERR(svn_sqlite__step_row(stmt)); /* Parent must exist as 'normal' */
11166
11167 parent_op_depth = svn_sqlite__column_int(stmt, 0);
11168
11169 if (parent_op_depth >= replaced_op_depth)
11170 {
11171 /* Did we replace inside our directory? */
11172
11173 *is_replace_root_p = (parent_op_depth == replaced_op_depth);
11174 SVN_ERR(svn_sqlite__reset(stmt));
11175 return SVN_NO_ERROR;
11176 }
11177
11178 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11179
11180 if (have_row)
11181 parent_op_depth = svn_sqlite__column_int(stmt, 0);
11182
11183 SVN_ERR(svn_sqlite__reset(stmt));
11184
11185 if (!have_row)
11186 *is_replace_root_p = TRUE; /* Parent is no replacement */
11187 else if (parent_op_depth < replaced_op_depth)
11188 *is_replace_root_p = TRUE; /* Parent replaces a lower layer */
11189 /*else // No replacement root */
11190 }
11191
11192 return SVN_NO_ERROR;
11193 }
11194
11195 svn_error_t *
svn_wc__db_node_check_replace(svn_boolean_t * is_replace_root,svn_boolean_t * base_replace,svn_boolean_t * is_replace,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)11196 svn_wc__db_node_check_replace(svn_boolean_t *is_replace_root,
11197 svn_boolean_t *base_replace,
11198 svn_boolean_t *is_replace,
11199 svn_wc__db_t *db,
11200 const char *local_abspath,
11201 apr_pool_t *scratch_pool)
11202 {
11203 svn_wc__db_wcroot_t *wcroot;
11204 const char *local_relpath;
11205
11206 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11207
11208 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11209 local_abspath,
11210 scratch_pool, scratch_pool));
11211 VERIFY_USABLE_WCROOT(wcroot);
11212
11213 if (is_replace_root)
11214 *is_replace_root = FALSE;
11215 if (base_replace)
11216 *base_replace = FALSE;
11217 if (is_replace)
11218 *is_replace = FALSE;
11219
11220 if (local_relpath[0] == '\0')
11221 return SVN_NO_ERROR; /* Working copy root can't be replaced */
11222
11223 SVN_WC__DB_WITH_TXN(
11224 check_replace_txn(is_replace_root, base_replace, is_replace,
11225 wcroot, local_relpath, scratch_pool),
11226 wcroot);
11227
11228 return SVN_NO_ERROR;
11229 }
11230
11231 svn_error_t *
svn_wc__db_read_children(const apr_array_header_t ** children,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11232 svn_wc__db_read_children(const apr_array_header_t **children,
11233 svn_wc__db_t *db,
11234 const char *local_abspath,
11235 apr_pool_t *result_pool,
11236 apr_pool_t *scratch_pool)
11237 {
11238 svn_wc__db_wcroot_t *wcroot;
11239 const char *local_relpath;
11240
11241 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11242
11243 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11244 local_abspath,
11245 scratch_pool, scratch_pool));
11246 VERIFY_USABLE_WCROOT(wcroot);
11247
11248 return gather_children(children, wcroot, local_relpath,
11249 STMT_SELECT_NODE_CHILDREN, -1,
11250 result_pool, scratch_pool);
11251 }
11252
11253
11254 /* Implementation of svn_wc__db_global_relocate */
11255 static svn_error_t *
relocate_txn(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,const char * repos_root_url,apr_pool_t * scratch_pool)11256 relocate_txn(svn_wc__db_wcroot_t *wcroot,
11257 const char *local_relpath,
11258 const char *repos_root_url,
11259 apr_pool_t *scratch_pool)
11260 {
11261 svn_sqlite__stmt_t *stmt;
11262 apr_int64_t new_repos_id;
11263 const char *local_dir_relpath;
11264 svn_wc__db_status_t status;
11265 const char *repos_uuid;
11266 svn_boolean_t have_base_node;
11267 apr_int64_t old_repos_id;
11268
11269 local_dir_relpath = local_relpath;
11270
11271 SVN_ERR(read_info(&status,
11272 NULL, NULL, NULL, &old_repos_id,
11273 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11274 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11275 NULL,
11276 &have_base_node, NULL, NULL,
11277 wcroot, local_relpath,
11278 scratch_pool, scratch_pool));
11279
11280 if (status == svn_wc__db_status_excluded)
11281 {
11282 /* The parent cannot be excluded, so look at the parent and then
11283 adjust the relpath */
11284 const char *parent_relpath = svn_relpath_dirname(local_dir_relpath,
11285 scratch_pool);
11286 SVN_ERR(read_info(&status,
11287 NULL, NULL, NULL, &old_repos_id,
11288 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11289 NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11290 NULL, NULL, NULL,
11291 NULL, NULL, NULL,
11292 wcroot, parent_relpath,
11293 scratch_pool, scratch_pool));
11294 local_dir_relpath = parent_relpath;
11295 }
11296
11297 if (old_repos_id == INVALID_REPOS_ID)
11298 {
11299 /* Do we need to support relocating something that is
11300 added/deleted/excluded without relocating the parent? If not
11301 then perhaps relpath, root_url and uuid should be passed down
11302 to the children so that they don't have to scan? */
11303
11304 if (status == svn_wc__db_status_deleted)
11305 {
11306 const char *work_del_relpath;
11307
11308 SVN_ERR(scan_deletion(NULL, NULL,
11309 &work_del_relpath, NULL,
11310 wcroot, local_dir_relpath,
11311 scratch_pool,
11312 scratch_pool));
11313 if (work_del_relpath)
11314 {
11315 /* Deleted within a copy/move */
11316
11317 /* The parent of the delete is added. */
11318 status = svn_wc__db_status_added;
11319 local_dir_relpath = svn_relpath_dirname(work_del_relpath,
11320 scratch_pool);
11321 }
11322 }
11323
11324 if (status == svn_wc__db_status_added)
11325 {
11326 SVN_ERR(scan_addition(NULL, NULL, NULL, &old_repos_id,
11327 NULL, NULL, NULL, NULL, NULL, NULL,
11328 wcroot, local_dir_relpath,
11329 scratch_pool, scratch_pool));
11330 }
11331 else
11332 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL,
11333 &old_repos_id,
11334 NULL, NULL, NULL, NULL, NULL,
11335 NULL, NULL, NULL, NULL, NULL,
11336 wcroot, local_dir_relpath,
11337 scratch_pool, scratch_pool));
11338 }
11339
11340 SVN_ERR(svn_wc__db_fetch_repos_info(NULL, &repos_uuid, wcroot,
11341 old_repos_id, scratch_pool));
11342 SVN_ERR_ASSERT(repos_uuid);
11343
11344 /* This function affects all the children of the given local_relpath,
11345 but the way that it does this is through the repos inheritance mechanism.
11346 So, we only need to rewrite the repos_id of the given local_relpath,
11347 as well as any children with a non-null repos_id, as well as various
11348 repos_id fields in the locks and working_node tables.
11349 */
11350
11351 /* Get the repos_id for the new repository. */
11352 SVN_ERR(create_repos_id(&new_repos_id, repos_root_url, repos_uuid,
11353 wcroot->sdb, scratch_pool));
11354
11355 /* Set the (base and working) repos_ids and clear the dav_caches */
11356 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11357 STMT_RECURSIVE_UPDATE_NODE_REPO));
11358 SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
11359 old_repos_id, new_repos_id));
11360 SVN_ERR(svn_sqlite__step_done(stmt));
11361
11362 if (have_base_node)
11363 {
11364 /* Update any locks for the root or its children. */
11365 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11366 STMT_UPDATE_LOCK_REPOS_ID));
11367 SVN_ERR(svn_sqlite__bindf(stmt, "ii", old_repos_id, new_repos_id));
11368 SVN_ERR(svn_sqlite__step_done(stmt));
11369 }
11370
11371 /* ### TODO: Update urls stored in inherited properties...
11372 What about urls in conflicts?
11373 # We can probably keep these as they are only used
11374 for showing full urls to the user */
11375
11376 return SVN_NO_ERROR;
11377 }
11378
11379
11380 svn_error_t *
svn_wc__db_global_relocate(svn_wc__db_t * db,const char * local_dir_abspath,const char * repos_root_url,apr_pool_t * scratch_pool)11381 svn_wc__db_global_relocate(svn_wc__db_t *db,
11382 const char *local_dir_abspath,
11383 const char *repos_root_url,
11384 apr_pool_t *scratch_pool)
11385 {
11386 svn_wc__db_wcroot_t *wcroot;
11387 const char *local_relpath;
11388
11389 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
11390 /* ### assert that we were passed a directory? */
11391
11392 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
11393 db, local_dir_abspath, scratch_pool, scratch_pool));
11394 VERIFY_USABLE_WCROOT(wcroot);
11395
11396 SVN_WC__DB_WITH_TXN(
11397 relocate_txn(wcroot, local_relpath, repos_root_url, scratch_pool),
11398 wcroot);
11399
11400 SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_infinity,
11401 scratch_pool));
11402
11403 return SVN_NO_ERROR;
11404 }
11405
11406
11407 /* Helper for commit_node()
11408 Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of
11409 (WCROOT, LOCAL_RELPATH), directly if its BASE row exists or implied from
11410 its parent's BASE row if not. In the latter case, error if the parent
11411 BASE row does not exist. */
11412 static svn_error_t *
determine_commit_repos_info(apr_int64_t * repos_id,const char ** repos_relpath,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11413 determine_commit_repos_info(apr_int64_t *repos_id,
11414 const char **repos_relpath,
11415 svn_wc__db_wcroot_t *wcroot,
11416 const char *local_relpath,
11417 apr_pool_t *result_pool,
11418 apr_pool_t *scratch_pool)
11419 {
11420 svn_sqlite__stmt_t *stmt;
11421 svn_boolean_t have_row;
11422 int op_depth;
11423
11424 /* Prefer the current node's repository information. */
11425 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11426 STMT_SELECT_NODE_INFO));
11427 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11428 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11429
11430 if (!have_row)
11431 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
11432 svn_sqlite__reset(stmt),
11433 _("The node '%s' was not found."),
11434 path_for_error_message(wcroot, local_relpath,
11435 scratch_pool));
11436
11437 op_depth = svn_sqlite__column_int(stmt, 0);
11438
11439 if (op_depth > 0)
11440 {
11441 svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 3,
11442 presence_map);
11443
11444 if (presence == svn_wc__db_status_base_deleted)
11445 {
11446 SVN_ERR(svn_sqlite__step_row(stmt)); /* There must be a row */
11447 op_depth = svn_sqlite__column_int(stmt, 0);
11448 }
11449 else
11450 {
11451 const char *parent_repos_relpath;
11452 const char *parent_relpath;
11453 const char *name;
11454
11455 SVN_ERR(svn_sqlite__reset(stmt));
11456
11457 /* The repository relative path of an add/copy is based on its
11458 ancestor, not on the shadowed base layer.
11459
11460 As this function is only used from the commit processing we know
11461 the parent directory has only a BASE row, so we can just obtain
11462 the information directly by recursing (once!) */
11463
11464 svn_relpath_split(&parent_relpath, &name, local_relpath,
11465 scratch_pool);
11466
11467 SVN_ERR(determine_commit_repos_info(repos_id, &parent_repos_relpath,
11468 wcroot, parent_relpath,
11469 scratch_pool, scratch_pool));
11470
11471 *repos_relpath = svn_relpath_join(parent_repos_relpath, name,
11472 result_pool);
11473 return SVN_NO_ERROR;
11474 }
11475 }
11476
11477
11478 SVN_ERR_ASSERT(op_depth == 0); /* And that row must be BASE */
11479
11480 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1));
11481 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 2));
11482
11483 *repos_id = svn_sqlite__column_int64(stmt, 1);
11484 *repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
11485
11486 return svn_error_trace(svn_sqlite__reset(stmt));
11487 }
11488
11489 static svn_error_t *
moved_descendant_collect(apr_hash_t ** map,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int op_depth,apr_pool_t * result_pool,apr_pool_t * scratch_pool)11490 moved_descendant_collect(apr_hash_t **map,
11491 svn_wc__db_wcroot_t *wcroot,
11492 const char *local_relpath,
11493 int op_depth,
11494 apr_pool_t *result_pool,
11495 apr_pool_t *scratch_pool)
11496 {
11497 svn_sqlite__stmt_t *stmt;
11498 svn_boolean_t have_row;
11499
11500 *map = NULL;
11501
11502 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11503 STMT_SELECT_MOVED_DESCENDANTS_SRC));
11504 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
11505 local_relpath,
11506 op_depth));
11507
11508 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11509 if (! have_row)
11510 return svn_error_trace(svn_sqlite__reset(stmt));
11511
11512 /* Find all moved descendants. Key them on target, because that is
11513 always unique */
11514 while (have_row)
11515 {
11516 const char *src_relpath = svn_sqlite__column_text(stmt, 1, result_pool);
11517 const char *to_relpath = svn_sqlite__column_text(stmt, 4, result_pool);
11518
11519 if (!*map)
11520 *map = apr_hash_make(result_pool);
11521
11522 svn_hash_sets(*map, to_relpath, src_relpath);
11523
11524 SVN_ERR(svn_sqlite__step(&have_row, stmt));
11525 }
11526 SVN_ERR(svn_sqlite__reset(stmt));
11527
11528 return SVN_NO_ERROR;
11529 }
11530
11531 /* Helper for svn_wc__db_global_commit()
11532
11533 Makes local_relpath and all its descendants at the same op-depth represent
11534 the copy origin repos_id:repos_relpath@revision.
11535
11536 This code is only valid to fix-up a move from an old location, to a new
11537 location during a commit.
11538
11539 Assumptions:
11540 * local_relpath is not the working copy root (can't be moved)
11541 * repos_relpath is not the repository root (can't be moved)
11542 */
11543 static svn_error_t *
moved_descendant_commit(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_int64_t repos_id,const char * repos_relpath,svn_revnum_t revision,apr_hash_t * children,apr_pool_t * scratch_pool)11544 moved_descendant_commit(svn_wc__db_wcroot_t *wcroot,
11545 const char *local_relpath,
11546 apr_int64_t repos_id,
11547 const char *repos_relpath,
11548 svn_revnum_t revision,
11549 apr_hash_t *children,
11550 apr_pool_t *scratch_pool)
11551 {
11552 apr_pool_t *iterpool;
11553 svn_sqlite__stmt_t *stmt;
11554 apr_hash_index_t *hi;
11555
11556 SVN_ERR_ASSERT(*local_relpath != '\0'
11557 && *repos_relpath != '\0');
11558
11559 if (!children)
11560 return SVN_NO_ERROR;
11561
11562 /* Then update them */
11563 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11564 STMT_COMMIT_UPDATE_ORIGIN));
11565
11566 iterpool = svn_pool_create(scratch_pool);
11567 for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
11568 {
11569 const char *src_relpath = apr_hash_this_val(hi);
11570 const char *to_relpath = apr_hash_this_key(hi);
11571 const char *new_repos_relpath;
11572 int to_op_depth = relpath_depth(to_relpath);
11573 int affected;
11574 apr_hash_t *map;
11575
11576 svn_pool_clear(iterpool);
11577
11578 SVN_ERR_ASSERT(to_op_depth > 0);
11579
11580 new_repos_relpath = svn_relpath_join(
11581 repos_relpath,
11582 svn_relpath_skip_ancestor(local_relpath,
11583 src_relpath),
11584 iterpool);
11585
11586 SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11587 to_relpath,
11588 to_op_depth,
11589 repos_id,
11590 new_repos_relpath,
11591 revision));
11592 SVN_ERR(svn_sqlite__update(&affected, stmt));
11593
11594 #ifdef SVN_DEBUG
11595 /* Enable in release code?
11596 Broken moves are not fatal yet, but this assertion would break
11597 committing them */
11598 SVN_ERR_ASSERT(affected >= 1); /* If this fails there is no move dest */
11599 #endif
11600
11601 SVN_ERR(moved_descendant_collect(&map, wcroot, to_relpath, to_op_depth,
11602 iterpool, iterpool));
11603 SVN_ERR(moved_descendant_commit(wcroot, to_relpath,
11604 repos_id, new_repos_relpath, revision,
11605 map, iterpool));
11606 }
11607
11608 svn_pool_destroy(iterpool);
11609 return SVN_NO_ERROR;
11610 }
11611
11612 /* Helper for svn_wc__db_global_commit()
11613
11614 Moves all nodes below LOCAL_RELPATH from op-depth OP_DEPTH to op-depth 0
11615 (BASE), setting their presence to 'not-present' if their presence wasn't
11616 'normal'.
11617
11618 Makes all nodes below LOCAL_RELPATH represent the descendants of repository
11619 location repos_id:repos_relpath@revision.
11620
11621 Assumptions:
11622 * local_relpath is not the working copy root (can't be replaced)
11623 * repos_relpath is not the repository root (can't be replaced)
11624 */
11625 static svn_error_t *
descendant_commit(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int op_depth,apr_int64_t repos_id,const char * repos_relpath,svn_revnum_t revision,apr_pool_t * scratch_pool)11626 descendant_commit(svn_wc__db_wcroot_t *wcroot,
11627 const char *local_relpath,
11628 int op_depth,
11629 apr_int64_t repos_id,
11630 const char *repos_relpath,
11631 svn_revnum_t revision,
11632 apr_pool_t *scratch_pool)
11633 {
11634 svn_sqlite__stmt_t *stmt;
11635
11636 SVN_ERR_ASSERT(*local_relpath != '\0'
11637 && *repos_relpath != '\0');
11638
11639 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11640 STMT_COMMIT_DESCENDANTS_TO_BASE));
11641
11642 SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11643 local_relpath,
11644 op_depth,
11645 repos_id,
11646 repos_relpath,
11647 revision));
11648
11649 SVN_ERR(svn_sqlite__update(NULL, stmt));
11650
11651 return SVN_NO_ERROR;
11652 }
11653
11654 /* The body of svn_wc__db_global_commit().
11655 */
11656 static svn_error_t *
commit_node(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_revnum_t new_revision,svn_revnum_t changed_rev,apr_time_t changed_date,const char * changed_author,const svn_checksum_t * new_checksum,apr_hash_t * new_dav_cache,svn_boolean_t keep_changelist,svn_boolean_t no_unlock,const svn_skel_t * work_items,apr_pool_t * scratch_pool)11657 commit_node(svn_wc__db_wcroot_t *wcroot,
11658 const char *local_relpath,
11659 svn_revnum_t new_revision,
11660 svn_revnum_t changed_rev,
11661 apr_time_t changed_date,
11662 const char *changed_author,
11663 const svn_checksum_t *new_checksum,
11664 apr_hash_t *new_dav_cache,
11665 svn_boolean_t keep_changelist,
11666 svn_boolean_t no_unlock,
11667 const svn_skel_t *work_items,
11668 apr_pool_t *scratch_pool)
11669 {
11670 svn_sqlite__stmt_t *stmt_info;
11671 svn_sqlite__stmt_t *stmt_act;
11672 svn_boolean_t have_act;
11673 svn_string_t prop_blob = { 0 };
11674 svn_string_t inherited_prop_blob = { 0 };
11675 const char *changelist = NULL;
11676 const char *parent_relpath;
11677 svn_wc__db_status_t new_presence;
11678 svn_node_kind_t new_kind;
11679 const char *new_depth_str = NULL;
11680 svn_sqlite__stmt_t *stmt;
11681 apr_int64_t repos_id;
11682 const char *repos_relpath;
11683 int op_depth;
11684 svn_wc__db_status_t old_presence;
11685 svn_boolean_t moved_here;
11686
11687 /* If we are adding a file or directory, then we need to get
11688 repository information from the parent node since "this node" does
11689 not have a BASE).
11690
11691 For existing nodes, we should retain the (potentially-switched)
11692 repository information. */
11693 SVN_ERR(determine_commit_repos_info(&repos_id, &repos_relpath,
11694 wcroot, local_relpath,
11695 scratch_pool, scratch_pool));
11696
11697 /* ### is it better to select only the data needed? */
11698 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
11699 STMT_SELECT_NODE_INFO));
11700 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
11701 SVN_ERR(svn_sqlite__step_row(stmt_info));
11702
11703 SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
11704 STMT_SELECT_ACTUAL_NODE));
11705 SVN_ERR(svn_sqlite__bindf(stmt_act, "is",
11706 wcroot->wc_id, local_relpath));
11707 SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
11708
11709 /* There should be something to commit! */
11710
11711 op_depth = svn_sqlite__column_int(stmt_info, 0);
11712
11713 /* Figure out the new node's kind. It will be whatever is in WORKING_NODE,
11714 or there will be a BASE_NODE that has it. */
11715 old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map);
11716 new_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
11717
11718 /* What will the new depth be? */
11719 if (new_kind == svn_node_dir)
11720 new_depth_str = svn_sqlite__column_text(stmt_info, 11, scratch_pool);
11721
11722 /* Check that the repository information is not being changed. */
11723 if (op_depth == 0)
11724 {
11725 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 1));
11726 SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 2));
11727
11728 /* A commit cannot change these values. */
11729 SVN_ERR_ASSERT(repos_id == svn_sqlite__column_int64(stmt_info, 1));
11730 SVN_ERR_ASSERT(strcmp(repos_relpath,
11731 svn_sqlite__column_text(stmt_info, 2, NULL)) == 0);
11732 }
11733
11734 if (old_presence != svn_wc__db_status_base_deleted)
11735 {
11736 /* Find the appropriate new properties -- ACTUAL overrides any properties
11737 in WORKING that arrived as part of a copy/move.
11738
11739 Note: we'll keep them as a big blob of data, rather than
11740 deserialize/serialize them. */
11741 if (have_act)
11742 prop_blob.data = svn_sqlite__column_blob(stmt_act, 1, &prop_blob.len,
11743 scratch_pool);
11744 if (prop_blob.data == NULL)
11745 prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len,
11746 scratch_pool);
11747
11748 inherited_prop_blob.data = svn_sqlite__column_blob(
11749 stmt_info, 16,
11750 &inherited_prop_blob.len,
11751 scratch_pool);
11752
11753 if (keep_changelist && have_act)
11754 changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool);
11755
11756 moved_here = svn_sqlite__column_int(stmt_info, 15);
11757 }
11758 else
11759 {
11760 moved_here = FALSE;
11761 changelist = NULL;
11762 }
11763
11764 /* ### other stuff? */
11765
11766 SVN_ERR(svn_sqlite__reset(stmt_info));
11767 SVN_ERR(svn_sqlite__reset(stmt_act));
11768
11769 if (op_depth > 0)
11770 {
11771 int affected_rows;
11772
11773 SVN_ERR_ASSERT(op_depth == relpath_depth(local_relpath));
11774
11775 /* First clear the moves that we are going to delete in a bit */
11776 {
11777 apr_hash_t *old_moves;
11778 apr_hash_index_t *hi;
11779 SVN_ERR(moved_descendant_collect(&old_moves, wcroot, local_relpath, 0,
11780 scratch_pool, scratch_pool));
11781
11782 if (old_moves)
11783 for (hi = apr_hash_first(scratch_pool, old_moves);
11784 hi; hi = apr_hash_next(hi))
11785 {
11786 SVN_ERR(clear_moved_here(wcroot, apr_hash_this_key(hi),
11787 scratch_pool));
11788 }
11789 }
11790
11791 /* This removes all layers of this node and at the same time determines
11792 if we need to remove shadowed layers below our descendants. */
11793
11794 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11795 STMT_DELETE_NODE_ALL_LAYERS));
11796 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11797 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
11798
11799 if (affected_rows > 1)
11800 {
11801 /* We commit a shadowing operation
11802
11803 1) Remove all shadowed nodes
11804 2) And remove all nodes that have a base-deleted as lowest layer,
11805 because 1) removed that layer */
11806
11807 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11808 STMT_DELETE_SHADOWED_RECURSIVE));
11809
11810 SVN_ERR(svn_sqlite__bindf(stmt,
11811 "isd",
11812 wcroot->wc_id,
11813 local_relpath,
11814 op_depth));
11815
11816 SVN_ERR(svn_sqlite__step_done(stmt));
11817 }
11818
11819 /* Note that while these two calls look so similar that they might
11820 be integrated, they really affect a different op-depth and
11821 completely different nodes (via a different recursion pattern). */
11822
11823 if (old_presence != svn_wc__db_status_base_deleted)
11824 {
11825 /* Collapse descendants of the current op_depth to layer 0,
11826 this includes moved-from/to clearing */
11827 SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth,
11828 repos_id, repos_relpath, new_revision,
11829 scratch_pool));
11830 }
11831
11832 if (old_presence != svn_wc__db_status_base_deleted)
11833 {
11834 apr_hash_t *moves = NULL;
11835
11836 SVN_ERR(moved_descendant_collect(&moves, wcroot, local_relpath, 0,
11837 scratch_pool, scratch_pool));
11838
11839 /* And make the recorded local moves represent moves of the node we
11840 just committed. */
11841 SVN_ERR(moved_descendant_commit(wcroot, local_relpath,
11842 repos_id, repos_relpath, new_revision,
11843 moves, scratch_pool));
11844 }
11845
11846 if (moved_here)
11847 {
11848 /* This node is no longer modified, so no node was moved here */
11849 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11850 STMT_CLEAR_MOVED_TO_FROM_DEST));
11851 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
11852 local_relpath));
11853
11854 SVN_ERR(svn_sqlite__step_done(stmt));
11855 }
11856 }
11857 /* Update or add the BASE_NODE row with all the new information. */
11858
11859 if (*local_relpath == '\0')
11860 parent_relpath = NULL;
11861 else
11862 parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
11863
11864 /* Preserve any incomplete status */
11865 if (old_presence != svn_wc__db_status_base_deleted)
11866 {
11867 new_presence = (old_presence == svn_wc__db_status_incomplete
11868 ? svn_wc__db_status_incomplete
11869 : svn_wc__db_status_normal);
11870
11871 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11872 STMT_APPLY_CHANGES_TO_BASE_NODE));
11873 /* symlink_target not yet used */
11874 SVN_ERR(svn_sqlite__bindf(stmt, "issisrtstrisnbn",
11875 wcroot->wc_id, local_relpath,
11876 parent_relpath,
11877 repos_id,
11878 repos_relpath,
11879 new_revision,
11880 presence_map, new_presence,
11881 new_depth_str,
11882 kind_map, new_kind,
11883 changed_rev,
11884 changed_date,
11885 changed_author,
11886 prop_blob.data, prop_blob.len));
11887
11888 SVN_ERR(svn_sqlite__bind_checksum(stmt, 13, new_checksum,
11889 scratch_pool));
11890 SVN_ERR(svn_sqlite__bind_properties(stmt, 15, new_dav_cache,
11891 scratch_pool));
11892 if (inherited_prop_blob.data != NULL)
11893 {
11894 SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data,
11895 inherited_prop_blob.len));
11896 }
11897
11898 SVN_ERR(svn_sqlite__step_done(stmt));
11899 }
11900 else
11901 {
11902 struct insert_base_baton_t ibb;
11903 blank_ibb(&ibb);
11904
11905 ibb.repos_id = repos_id;
11906 ibb.status = svn_wc__db_status_not_present;
11907 ibb.kind = new_kind;
11908 ibb.repos_relpath = repos_relpath;
11909 ibb.revision = new_revision;
11910
11911 SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
11912
11913 keep_changelist = FALSE; /* Nothing there */
11914 }
11915
11916 if (have_act)
11917 {
11918 if (keep_changelist && changelist != NULL)
11919 {
11920 /* The user told us to keep the changelist. Replace the row in
11921 ACTUAL_NODE with the basic keys and the changelist. */
11922 SVN_ERR(svn_sqlite__get_statement(
11923 &stmt, wcroot->sdb,
11924 STMT_RESET_ACTUAL_WITH_CHANGELIST));
11925 SVN_ERR(svn_sqlite__bindf(stmt, "isss",
11926 wcroot->wc_id, local_relpath,
11927 svn_relpath_dirname(local_relpath,
11928 scratch_pool),
11929 changelist));
11930 SVN_ERR(svn_sqlite__step_done(stmt));
11931 }
11932 else
11933 {
11934 /* Toss the ACTUAL_NODE row. */
11935 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11936 STMT_DELETE_ACTUAL_NODE));
11937 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11938 SVN_ERR(svn_sqlite__step_done(stmt));
11939 }
11940 }
11941
11942 if (!no_unlock)
11943 {
11944 svn_sqlite__stmt_t *lock_stmt;
11945 svn_boolean_t op_root = (op_depth > 0
11946 && (relpath_depth(local_relpath) == op_depth));
11947
11948 /* If we are committing an add of a delete, we can assume we own
11949 all locks at or below REPOS_RELPATH (or the server would have
11950 denied the commit). As we must have passed these to the server
11951 we can now safely remove them.
11952 */
11953 SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
11954 op_root
11955 ? STMT_DELETE_LOCK_RECURSIVELY
11956 : STMT_DELETE_LOCK));
11957 SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
11958 SVN_ERR(svn_sqlite__step_done(lock_stmt));
11959 }
11960
11961 /* Install any work items into the queue, as part of this transaction. */
11962 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
11963
11964 return SVN_NO_ERROR;
11965 }
11966
11967
11968 svn_error_t *
svn_wc__db_global_commit(svn_wc__db_t * db,const char * local_abspath,svn_revnum_t new_revision,svn_revnum_t changed_revision,apr_time_t changed_date,const char * changed_author,const svn_checksum_t * new_checksum,apr_hash_t * new_dav_cache,svn_boolean_t keep_changelist,svn_boolean_t no_unlock,const svn_skel_t * work_items,apr_pool_t * scratch_pool)11969 svn_wc__db_global_commit(svn_wc__db_t *db,
11970 const char *local_abspath,
11971 svn_revnum_t new_revision,
11972 svn_revnum_t changed_revision,
11973 apr_time_t changed_date,
11974 const char *changed_author,
11975 const svn_checksum_t *new_checksum,
11976 apr_hash_t *new_dav_cache,
11977 svn_boolean_t keep_changelist,
11978 svn_boolean_t no_unlock,
11979 const svn_skel_t *work_items,
11980 apr_pool_t *scratch_pool)
11981 {
11982 const char *local_relpath;
11983 svn_wc__db_wcroot_t *wcroot;
11984
11985 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11986 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
11987
11988 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11989 local_abspath, scratch_pool, scratch_pool));
11990 VERIFY_USABLE_WCROOT(wcroot);
11991
11992 SVN_WC__DB_WITH_TXN(
11993 commit_node(wcroot, local_relpath,
11994 new_revision, changed_revision, changed_date, changed_author,
11995 new_checksum, new_dav_cache, keep_changelist,
11996 no_unlock, work_items, scratch_pool),
11997 wcroot);
11998
11999 /* We *totally* monkeyed the entries. Toss 'em. */
12000 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
12001
12002 return SVN_NO_ERROR;
12003 }
12004
12005
12006 svn_error_t *
svn_wc__db_global_update(svn_wc__db_t * db,const char * local_abspath,svn_node_kind_t new_kind,const char * new_repos_relpath,svn_revnum_t new_revision,const apr_hash_t * new_props,svn_revnum_t new_changed_rev,apr_time_t new_changed_date,const char * new_changed_author,const apr_array_header_t * new_children,const svn_checksum_t * new_checksum,const char * new_target,const apr_hash_t * new_dav_cache,const svn_skel_t * conflict,const svn_skel_t * work_items,apr_pool_t * scratch_pool)12007 svn_wc__db_global_update(svn_wc__db_t *db,
12008 const char *local_abspath,
12009 svn_node_kind_t new_kind,
12010 const char *new_repos_relpath,
12011 svn_revnum_t new_revision,
12012 const apr_hash_t *new_props,
12013 svn_revnum_t new_changed_rev,
12014 apr_time_t new_changed_date,
12015 const char *new_changed_author,
12016 const apr_array_header_t *new_children,
12017 const svn_checksum_t *new_checksum,
12018 const char *new_target,
12019 const apr_hash_t *new_dav_cache,
12020 const svn_skel_t *conflict,
12021 const svn_skel_t *work_items,
12022 apr_pool_t *scratch_pool)
12023 {
12024 NOT_IMPLEMENTED();
12025
12026 #if 0
12027 svn_wc__db_wcroot_t *wcroot;
12028 const char *local_relpath;
12029
12030 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12031 /* ### allow NULL for NEW_REPOS_RELPATH to indicate "no change"? */
12032 SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
12033 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
12034 SVN_ERR_ASSERT(new_props != NULL);
12035 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_changed_rev));
12036 SVN_ERR_ASSERT((new_children != NULL
12037 && new_checksum == NULL
12038 && new_target == NULL)
12039 || (new_children == NULL
12040 && new_checksum != NULL
12041 && new_target == NULL)
12042 || (new_children == NULL
12043 && new_checksum == NULL
12044 && new_target != NULL));
12045
12046 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12047 local_abspath, scratch_pool, scratch_pool));
12048 VERIFY_USABLE_WCROOT(wcroot);
12049
12050 SVN_WC__DB_WITH_TXN(
12051 update_node(wcroot, local_relpath,
12052 new_repos_relpath, new_revision, new_props,
12053 new_changed_rev, new_changed_date, new_changed_author,
12054 new_children, new_checksum, new_target,
12055 conflict, work_items, scratch_pool),
12056 wcroot);
12057
12058 /* We *totally* monkeyed the entries. Toss 'em. */
12059 SVN_ERR(flush_entries(wcroot, local_abspath, scratch_pool));
12060
12061 return SVN_NO_ERROR;
12062 #endif
12063 }
12064
12065 /* Sets a base nodes revision, repository relative path, and/or inherited
12066 propertis. If LOCAL_ABSPATH's rev (REV) is valid, set its revision. If
12067 SET_REPOS_RELPATH is TRUE set its repository relative path to REPOS_RELPATH
12068 (and make sure its REPOS_ID is still valid). If IPROPS is not NULL set its
12069 inherited properties to IPROPS, if IPROPS is NULL then clear any the iprops
12070 cache for the base node.
12071 */
12072 static svn_error_t *
db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_array_header_t * iprops,svn_revnum_t rev,svn_boolean_t set_repos_relpath,const char * repos_relpath,apr_int64_t repos_id,apr_pool_t * scratch_pool)12073 db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot,
12074 const char *local_relpath,
12075 apr_array_header_t *iprops,
12076 svn_revnum_t rev,
12077 svn_boolean_t set_repos_relpath,
12078 const char *repos_relpath,
12079 apr_int64_t repos_id,
12080 apr_pool_t *scratch_pool)
12081 {
12082 svn_sqlite__stmt_t *stmt;
12083
12084 SVN_ERR(flush_entries(wcroot,
12085 svn_dirent_join(wcroot->abspath, local_relpath,
12086 scratch_pool),
12087 svn_depth_empty, scratch_pool));
12088
12089
12090 if (SVN_IS_VALID_REVNUM(rev))
12091 {
12092 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12093 STMT_UPDATE_BASE_REVISION));
12094
12095 SVN_ERR(svn_sqlite__bindf(stmt, "isr", wcroot->wc_id, local_relpath,
12096 rev));
12097
12098 SVN_ERR(svn_sqlite__step_done(stmt));
12099 }
12100
12101 if (set_repos_relpath)
12102 {
12103 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12104 STMT_UPDATE_BASE_REPOS));
12105
12106 SVN_ERR(svn_sqlite__bindf(stmt, "isis", wcroot->wc_id, local_relpath,
12107 repos_id, repos_relpath));
12108
12109 SVN_ERR(svn_sqlite__step_done(stmt));
12110 }
12111
12112 /* Set or clear iprops. */
12113 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12114 STMT_UPDATE_IPROP));
12115 SVN_ERR(svn_sqlite__bindf(stmt, "is",
12116 wcroot->wc_id,
12117 local_relpath));
12118 SVN_ERR(svn_sqlite__bind_iprops(stmt, 3, iprops, scratch_pool));
12119 SVN_ERR(svn_sqlite__step_done(stmt));
12120
12121 return SVN_NO_ERROR;
12122 }
12123
12124 /* The main body of bump_revisions_post_update().
12125 *
12126 * Tweak the information for LOCAL_RELPATH in WCROOT. If NEW_REPOS_RELPATH is
12127 * non-NULL update the entry to the new url specified by NEW_REPOS_RELPATH,
12128 * NEW_REPOS_ID. If NEW_REV is valid, make this the node's working revision.
12129 *
12130 * NODE_STATUS, NODE_KIND, NODE_REVISION and NODE_REPOS_RELPATH represent the
12131 * values as stored currently in WCROOT for LOCAL_RELPATH.
12132 *
12133 * If WCROOT_IPROPS is not NULL it is a hash mapping const char * absolute
12134 * working copy paths to depth-first ordered arrays of
12135 * svn_prop_inherited_item_t * structures. If the absolute path equivalent
12136 * of LOCAL_RELPATH exists in WCROOT_IPROPS, then set the hashed value as the
12137 * node's inherited properties.
12138 *
12139 * Unless S_ROOT is TRUE the tweaks might cause the node for LOCAL_ABSPATH to
12140 * be removed from the WC; if IS_ROOT is TRUE this will not happen.
12141 */
12142 static svn_error_t *
bump_node_revision(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_wc__db_status_t node_status,svn_node_kind_t node_kind,svn_revnum_t node_revision,const char * node_repos_relpath,apr_int64_t new_repos_id,const char * new_repos_relpath,svn_revnum_t new_rev,svn_depth_t depth,apr_hash_t * exclude_relpaths,apr_hash_t * wcroot_iprops,svn_boolean_t is_root,svn_boolean_t skip_when_dir,svn_wc__db_t * db,apr_pool_t * scratch_pool)12143 bump_node_revision(svn_wc__db_wcroot_t *wcroot,
12144 const char *local_relpath,
12145 svn_wc__db_status_t node_status,
12146 svn_node_kind_t node_kind,
12147 svn_revnum_t node_revision,
12148 const char *node_repos_relpath,
12149 apr_int64_t new_repos_id,
12150 const char *new_repos_relpath,
12151 svn_revnum_t new_rev,
12152 svn_depth_t depth,
12153 apr_hash_t *exclude_relpaths,
12154 apr_hash_t *wcroot_iprops,
12155 svn_boolean_t is_root,
12156 svn_boolean_t skip_when_dir,
12157 svn_wc__db_t *db,
12158 apr_pool_t *scratch_pool)
12159 {
12160 apr_pool_t *iterpool;
12161 apr_hash_t *children;
12162 apr_hash_index_t *hi;
12163 svn_boolean_t set_repos_relpath = FALSE;
12164 svn_depth_t depth_below_here = depth;
12165 apr_array_header_t *iprops = NULL;
12166
12167 if (new_repos_relpath != NULL
12168 && strcmp(node_repos_relpath, new_repos_relpath))
12169 set_repos_relpath = TRUE;
12170
12171 if (wcroot_iprops)
12172 iprops = svn_hash_gets(wcroot_iprops,
12173 svn_dirent_join(wcroot->abspath, local_relpath,
12174 scratch_pool));
12175
12176 if (iprops
12177 || set_repos_relpath
12178 || (SVN_IS_VALID_REVNUM(new_rev) && new_rev != node_revision))
12179 {
12180 SVN_ERR(db_op_set_rev_repos_relpath_iprops(wcroot, local_relpath,
12181 iprops, new_rev,
12182 set_repos_relpath,
12183 new_repos_relpath,
12184 new_repos_id,
12185 scratch_pool));
12186 }
12187
12188 /* Early out */
12189 if (depth <= svn_depth_empty
12190 || node_kind != svn_node_dir
12191 || node_status == svn_wc__db_status_server_excluded
12192 || node_status == svn_wc__db_status_excluded
12193 || node_status == svn_wc__db_status_not_present)
12194 return SVN_NO_ERROR;
12195
12196 /* And now recurse over the children */
12197
12198 depth_below_here = depth;
12199
12200 if (depth == svn_depth_immediates || depth == svn_depth_files)
12201 depth_below_here = svn_depth_empty;
12202
12203 iterpool = svn_pool_create(scratch_pool);
12204
12205 SVN_ERR(base_get_children_info(&children, wcroot, local_relpath, 0,
12206 scratch_pool, iterpool));
12207 for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
12208 {
12209 const char *child_basename = apr_hash_this_key(hi);
12210 const struct svn_wc__db_base_info_t *child_info;
12211 const char *child_local_relpath;
12212 const char *child_repos_relpath = NULL;
12213
12214 svn_pool_clear(iterpool);
12215
12216 child_info = apr_hash_this_val(hi);
12217
12218 if (child_info->update_root && child_info->kind == svn_node_file)
12219 continue; /* Skip file externals */
12220
12221 if (depth < svn_depth_immediates && child_info->kind == svn_node_dir)
12222 continue; /* Skip directories */
12223
12224 child_local_relpath = svn_relpath_join(local_relpath, child_basename,
12225 iterpool);
12226
12227 /* Don't touch nodes that can't be touched via the exclude list */
12228 if (svn_hash_gets(exclude_relpaths, child_local_relpath))
12229 continue;
12230
12231 /* If the node is still marked 'not-present', then the server did not
12232 re-add it. So it's really gone in this revision, thus we remove the
12233 node.
12234
12235 If the node is still marked 'server-excluded' and yet is not the same
12236 revision as new_rev, then the server did not re-add it, nor
12237 re-server-exclude it, so we can remove the node. */
12238 if (child_info->status == svn_wc__db_status_not_present
12239 || (child_info->status == svn_wc__db_status_server_excluded &&
12240 child_info->revnum != new_rev))
12241 {
12242 svn_sqlite__stmt_t *stmt;
12243 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12244 STMT_DELETE_BASE_NODE));
12245 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, child_local_relpath));
12246 SVN_ERR(svn_sqlite__step_done(stmt));
12247 continue;
12248 }
12249
12250 /* Derive the new URL for the current (child) entry */
12251 if (new_repos_relpath)
12252 child_repos_relpath = svn_relpath_join(new_repos_relpath,
12253 child_basename, iterpool);
12254
12255 SVN_ERR(bump_node_revision(wcroot, child_local_relpath,
12256 child_info->status,
12257 child_info->kind,
12258 child_info->revnum,
12259 child_info->repos_relpath,
12260 new_repos_id,
12261 child_repos_relpath, new_rev,
12262 depth_below_here,
12263 exclude_relpaths, wcroot_iprops,
12264 FALSE /* is_root */,
12265 (depth < svn_depth_immediates), db,
12266 iterpool));
12267 }
12268
12269 /* Cleanup */
12270 svn_pool_destroy(iterpool);
12271
12272 return SVN_NO_ERROR;
12273 }
12274
12275 /* Helper for svn_wc__db_op_bump_revisions_post_update().
12276 */
12277 static svn_error_t *
bump_revisions_post_update(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_wc__db_t * db,svn_depth_t depth,const char * new_repos_relpath,const char * new_repos_root_url,const char * new_repos_uuid,svn_revnum_t new_revision,apr_hash_t * exclude_relpaths,apr_hash_t * wcroot_iprops,svn_boolean_t empty_update,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)12278 bump_revisions_post_update(svn_wc__db_wcroot_t *wcroot,
12279 const char *local_relpath,
12280 svn_wc__db_t *db,
12281 svn_depth_t depth,
12282 const char *new_repos_relpath,
12283 const char *new_repos_root_url,
12284 const char *new_repos_uuid,
12285 svn_revnum_t new_revision,
12286 apr_hash_t *exclude_relpaths,
12287 apr_hash_t *wcroot_iprops,
12288 svn_boolean_t empty_update,
12289 svn_wc_notify_func2_t notify_func,
12290 void *notify_baton,
12291 apr_pool_t *scratch_pool)
12292 {
12293 svn_wc__db_status_t status;
12294 svn_node_kind_t kind;
12295 svn_error_t *err;
12296 apr_int64_t new_repos_id = INVALID_REPOS_ID;
12297 svn_revnum_t revision;
12298 const char *repos_relpath;
12299
12300 err = svn_wc__db_base_get_info_internal(&status, &kind, &revision,
12301 &repos_relpath, NULL,
12302 NULL, NULL, NULL, NULL, NULL, NULL,
12303 NULL, NULL, NULL, NULL,
12304 wcroot, local_relpath,
12305 scratch_pool, scratch_pool);
12306 if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
12307 {
12308 svn_error_clear(err);
12309 return SVN_NO_ERROR;
12310 }
12311 else
12312 SVN_ERR(err);
12313
12314 switch (status)
12315 {
12316 case svn_wc__db_status_excluded:
12317 case svn_wc__db_status_server_excluded:
12318 case svn_wc__db_status_not_present:
12319 return SVN_NO_ERROR;
12320
12321 /* Explicitly ignore other statii */
12322 default:
12323 break;
12324 }
12325
12326 if (new_repos_root_url != NULL)
12327 SVN_ERR(create_repos_id(&new_repos_id, new_repos_root_url,
12328 new_repos_uuid,
12329 wcroot->sdb, scratch_pool));
12330
12331 SVN_ERR(bump_node_revision(wcroot, local_relpath,
12332 status, kind, revision, repos_relpath,
12333 new_repos_id,
12334 new_repos_relpath, new_revision,
12335 depth, exclude_relpaths,
12336 wcroot_iprops,
12337 TRUE /* is_root */, FALSE, db,
12338 scratch_pool));
12339
12340 /* ### TODO: Use empty_update flag for change knowledge */
12341 SVN_ERR(svn_wc__db_bump_moved_away(wcroot, local_relpath, depth, db,
12342 scratch_pool));
12343
12344 SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, SVN_INVALID_REVNUM,
12345 SVN_INVALID_REVNUM, notify_func,
12346 notify_baton, scratch_pool));
12347
12348 return SVN_NO_ERROR;
12349 }
12350
12351 svn_error_t *
svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t * db,const char * local_abspath,svn_depth_t depth,const char * new_repos_relpath,const char * new_repos_root_url,const char * new_repos_uuid,svn_revnum_t new_revision,apr_hash_t * exclude_relpaths,apr_hash_t * wcroot_iprops,svn_boolean_t empty_update,svn_wc_notify_func2_t notify_func,void * notify_baton,apr_pool_t * scratch_pool)12352 svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db,
12353 const char *local_abspath,
12354 svn_depth_t depth,
12355 const char *new_repos_relpath,
12356 const char *new_repos_root_url,
12357 const char *new_repos_uuid,
12358 svn_revnum_t new_revision,
12359 apr_hash_t *exclude_relpaths,
12360 apr_hash_t *wcroot_iprops,
12361 svn_boolean_t empty_update,
12362 svn_wc_notify_func2_t notify_func,
12363 void *notify_baton,
12364 apr_pool_t *scratch_pool)
12365 {
12366 const char *local_relpath;
12367 svn_wc__db_wcroot_t *wcroot;
12368
12369 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12370 local_abspath, scratch_pool, scratch_pool));
12371
12372 VERIFY_USABLE_WCROOT(wcroot);
12373
12374 if (svn_hash_gets(exclude_relpaths, local_relpath))
12375 return SVN_NO_ERROR;
12376
12377 if (depth == svn_depth_unknown)
12378 depth = svn_depth_infinity;
12379
12380 SVN_WC__DB_WITH_TXN(
12381 bump_revisions_post_update(wcroot, local_relpath, db,
12382 depth, new_repos_relpath, new_repos_root_url,
12383 new_repos_uuid, new_revision,
12384 exclude_relpaths, wcroot_iprops, empty_update,
12385 notify_func, notify_baton, scratch_pool),
12386 wcroot);
12387
12388 return SVN_NO_ERROR;
12389 }
12390
12391 /* The body of svn_wc__db_lock_add().
12392 */
12393 static svn_error_t *
lock_add_txn(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,const svn_wc__db_lock_t * lock,apr_pool_t * scratch_pool)12394 lock_add_txn(svn_wc__db_wcroot_t *wcroot,
12395 const char *local_relpath,
12396 const svn_wc__db_lock_t *lock,
12397 apr_pool_t *scratch_pool)
12398 {
12399 svn_sqlite__stmt_t *stmt;
12400 const char *repos_relpath;
12401 apr_int64_t repos_id;
12402
12403 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12404 &repos_relpath, &repos_id,
12405 NULL, NULL, NULL, NULL, NULL,
12406 NULL, NULL, NULL, NULL, NULL,
12407 wcroot, local_relpath,
12408 scratch_pool, scratch_pool));
12409
12410 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_LOCK));
12411 SVN_ERR(svn_sqlite__bindf(stmt, "iss",
12412 repos_id, repos_relpath, lock->token));
12413
12414 if (lock->owner != NULL)
12415 SVN_ERR(svn_sqlite__bind_text(stmt, 4, lock->owner));
12416
12417 if (lock->comment != NULL)
12418 SVN_ERR(svn_sqlite__bind_text(stmt, 5, lock->comment));
12419
12420 if (lock->date != 0)
12421 SVN_ERR(svn_sqlite__bind_int64(stmt, 6, lock->date));
12422
12423 SVN_ERR(svn_sqlite__insert(NULL, stmt));
12424
12425 return SVN_NO_ERROR;
12426 }
12427
12428
12429 svn_error_t *
svn_wc__db_lock_add(svn_wc__db_t * db,const char * local_abspath,const svn_wc__db_lock_t * lock,apr_pool_t * scratch_pool)12430 svn_wc__db_lock_add(svn_wc__db_t *db,
12431 const char *local_abspath,
12432 const svn_wc__db_lock_t *lock,
12433 apr_pool_t *scratch_pool)
12434 {
12435 svn_wc__db_wcroot_t *wcroot;
12436 const char *local_relpath;
12437
12438 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12439 SVN_ERR_ASSERT(lock != NULL);
12440
12441 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12442 local_abspath, scratch_pool, scratch_pool));
12443 VERIFY_USABLE_WCROOT(wcroot);
12444
12445 SVN_WC__DB_WITH_TXN(
12446 lock_add_txn(wcroot, local_relpath, lock, scratch_pool),
12447 wcroot);
12448
12449 /* There may be some entries, and the lock info is now out of date. */
12450 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
12451
12452 return SVN_NO_ERROR;
12453 }
12454
12455
12456 /* The body of svn_wc__db_lock_remove().
12457 */
12458 static svn_error_t *
lock_remove_txn(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_skel_t * work_items,apr_pool_t * scratch_pool)12459 lock_remove_txn(svn_wc__db_wcroot_t *wcroot,
12460 const char *local_relpath,
12461 svn_skel_t *work_items,
12462 apr_pool_t *scratch_pool)
12463 {
12464 const char *repos_relpath;
12465 apr_int64_t repos_id;
12466 svn_sqlite__stmt_t *stmt;
12467
12468 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12469 &repos_relpath, &repos_id,
12470 NULL, NULL, NULL, NULL, NULL,
12471 NULL, NULL, NULL, NULL, NULL,
12472 wcroot, local_relpath,
12473 scratch_pool, scratch_pool));
12474
12475 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12476 STMT_DELETE_LOCK));
12477 SVN_ERR(svn_sqlite__bindf(stmt, "is", repos_id, repos_relpath));
12478
12479 SVN_ERR(svn_sqlite__step_done(stmt));
12480
12481 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
12482
12483 return SVN_NO_ERROR;
12484 }
12485
12486
12487 svn_error_t *
svn_wc__db_lock_remove(svn_wc__db_t * db,const char * local_abspath,svn_skel_t * work_items,apr_pool_t * scratch_pool)12488 svn_wc__db_lock_remove(svn_wc__db_t *db,
12489 const char *local_abspath,
12490 svn_skel_t *work_items,
12491 apr_pool_t *scratch_pool)
12492 {
12493 svn_wc__db_wcroot_t *wcroot;
12494 const char *local_relpath;
12495
12496 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12497
12498 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12499 local_abspath, scratch_pool, scratch_pool));
12500 VERIFY_USABLE_WCROOT(wcroot);
12501
12502 SVN_WC__DB_WITH_TXN(
12503 lock_remove_txn(wcroot, local_relpath, work_items, scratch_pool),
12504 wcroot);
12505
12506 /* There may be some entries, and the lock info is now out of date. */
12507 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
12508
12509 return SVN_NO_ERROR;
12510 }
12511
12512 /* A helper for scan_addition().
12513 * Compute moved-from information for the node at LOCAL_RELPATH which
12514 * has been determined as having been moved-here.
12515 * If MOVED_FROM_RELPATH is not NULL, set *MOVED_FROM_RELPATH to the
12516 * path of the move-source node in *MOVED_FROM_RELPATH.
12517 * If DELETE_OP_ROOT_RELPATH is not NULL, set *DELETE_OP_ROOT_RELPATH
12518 * to the path of the op-root of the delete-half of the move.
12519 * If moved-from information cannot be derived, set both *MOVED_FROM_RELPATH
12520 * and *DELETE_OP_ROOT_RELPATH to NULL, and return a "copied" status.
12521 * COPY_OPT_ROOT_RELPATH is the relpath of the op-root of the copied-half
12522 * of the move. */
12523 static svn_error_t *
get_moved_from_info(const char ** moved_from_relpath,const char ** moved_from_op_root_relpath,const char * moved_to_op_root_relpath,int * op_depth,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12524 get_moved_from_info(const char **moved_from_relpath,
12525 const char **moved_from_op_root_relpath,
12526 const char *moved_to_op_root_relpath,
12527 int *op_depth,
12528 svn_wc__db_wcroot_t *wcroot,
12529 const char *local_relpath,
12530 apr_pool_t *result_pool,
12531 apr_pool_t *scratch_pool)
12532 {
12533 svn_sqlite__stmt_t *stmt;
12534 svn_boolean_t have_row;
12535
12536 /* Run a query to get the moved-from path from the DB. */
12537 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12538 STMT_SELECT_MOVED_FROM_RELPATH));
12539 SVN_ERR(svn_sqlite__bindf(stmt, "is",
12540 wcroot->wc_id, moved_to_op_root_relpath));
12541 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12542
12543 if (!have_row)
12544 {
12545 /* The move was only recorded at the copy-half, possibly because
12546 * the move operation was interrupted mid-way between the copy
12547 * and the delete. Treat this node as a normal copy. */
12548 if (moved_from_relpath)
12549 *moved_from_relpath = NULL;
12550 if (moved_from_op_root_relpath)
12551 *moved_from_op_root_relpath = NULL;
12552
12553 SVN_ERR(svn_sqlite__reset(stmt));
12554 return SVN_NO_ERROR;
12555 }
12556
12557 if (op_depth)
12558 *op_depth = svn_sqlite__column_int(stmt, 1);
12559
12560 if (moved_from_relpath || moved_from_op_root_relpath)
12561 {
12562 const char *db_delete_op_root_relpath;
12563
12564 /* The moved-from path from the DB is the relpath of
12565 * the op_root of the delete-half of the move. */
12566 db_delete_op_root_relpath = svn_sqlite__column_text(stmt, 0,
12567 result_pool);
12568 if (moved_from_op_root_relpath)
12569 *moved_from_op_root_relpath = db_delete_op_root_relpath;
12570
12571 if (moved_from_relpath)
12572 {
12573 if (strcmp(moved_to_op_root_relpath, local_relpath) == 0)
12574 {
12575 /* LOCAL_RELPATH is the op_root of the copied-half of the
12576 * move, so the correct MOVED_FROM_ABSPATH is the op-root
12577 * of the delete-half. */
12578 *moved_from_relpath = db_delete_op_root_relpath;
12579 }
12580 else
12581 {
12582 const char *child_relpath;
12583
12584 /* LOCAL_RELPATH is a child that was copied along with the
12585 * op_root of the copied-half of the move. Construct the
12586 * corresponding path beneath the op_root of the delete-half. */
12587
12588 /* Grab the child path relative to the op_root of the move
12589 * destination. */
12590 child_relpath = svn_relpath_skip_ancestor(
12591 moved_to_op_root_relpath, local_relpath);
12592
12593 SVN_ERR_ASSERT(child_relpath && strlen(child_relpath) > 0);
12594
12595 /* This join is valid because LOCAL_RELPATH has not been moved
12596 * within the copied-half of the move yet -- else, it would
12597 * be its own op_root. */
12598 *moved_from_relpath = svn_relpath_join(db_delete_op_root_relpath,
12599 child_relpath,
12600 result_pool);
12601 }
12602 }
12603 }
12604
12605 SVN_ERR(svn_sqlite__reset(stmt));
12606
12607 return SVN_NO_ERROR;
12608 }
12609
12610 /* Like svn_wc__db_scan_addition(), but with WCROOT+LOCAL_RELPATH instead of
12611 DB+LOCAL_ABSPATH.
12612
12613 The output value of *ORIGINAL_REPOS_ID will be INVALID_REPOS_ID if there
12614 is no 'copy-from' repository. */
12615 static svn_error_t *
scan_addition(svn_wc__db_status_t * status,const char ** op_root_relpath_p,const char ** repos_relpath,apr_int64_t * repos_id,const char ** original_repos_relpath,apr_int64_t * original_repos_id,svn_revnum_t * original_revision,const char ** moved_from_relpath,const char ** moved_from_op_root_relpath,int * moved_from_op_depth,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12616 scan_addition(svn_wc__db_status_t *status,
12617 const char **op_root_relpath_p,
12618 const char **repos_relpath,
12619 apr_int64_t *repos_id,
12620 const char **original_repos_relpath,
12621 apr_int64_t *original_repos_id,
12622 svn_revnum_t *original_revision,
12623 const char **moved_from_relpath,
12624 const char **moved_from_op_root_relpath,
12625 int *moved_from_op_depth,
12626 svn_wc__db_wcroot_t *wcroot,
12627 const char *local_relpath,
12628 apr_pool_t *result_pool,
12629 apr_pool_t *scratch_pool)
12630 {
12631 const char *op_root_relpath;
12632 const char *build_relpath = "";
12633
12634 /* Initialize most of the OUT parameters. Generally, we'll only be filling
12635 in a subset of these, so it is easier to init all up front. Note that
12636 the STATUS parameter will be initialized once we read the status of
12637 the specified node. */
12638 if (op_root_relpath_p)
12639 *op_root_relpath_p = NULL;
12640 if (original_repos_relpath)
12641 *original_repos_relpath = NULL;
12642 if (original_repos_id)
12643 *original_repos_id = INVALID_REPOS_ID;
12644 if (original_revision)
12645 *original_revision = SVN_INVALID_REVNUM;
12646 if (moved_from_relpath)
12647 *moved_from_relpath = NULL;
12648 if (moved_from_op_root_relpath)
12649 *moved_from_op_root_relpath = NULL;
12650 if (moved_from_op_depth)
12651 *moved_from_op_depth = 0;
12652
12653 {
12654 svn_sqlite__stmt_t *stmt;
12655 svn_boolean_t have_row;
12656 svn_wc__db_status_t presence;
12657 int op_depth;
12658 const char *repos_prefix_path;
12659
12660 /* ### is it faster to fetch fewer columns? */
12661 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12662 STMT_SELECT_WORKING_NODE));
12663 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
12664 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12665
12666 if (!have_row)
12667 {
12668 /* Reset statement before returning */
12669 SVN_ERR(svn_sqlite__reset(stmt));
12670
12671 /* ### maybe we should return a usage error instead? */
12672 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12673 _("The node '%s' was not found."),
12674 path_for_error_message(wcroot,
12675 local_relpath,
12676 scratch_pool));
12677 }
12678
12679 presence = svn_sqlite__column_token(stmt, 1, presence_map);
12680
12681 /* The starting node should exist normally. */
12682 op_depth = svn_sqlite__column_int(stmt, 0);
12683 if (op_depth == 0 || (presence != svn_wc__db_status_normal
12684 && presence != svn_wc__db_status_incomplete))
12685 /* reset the statement as part of the error generation process */
12686 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
12687 svn_sqlite__reset(stmt),
12688 _("Expected node '%s' to be added."),
12689 path_for_error_message(wcroot,
12690 local_relpath,
12691 scratch_pool));
12692
12693 if (original_revision)
12694 *original_revision = svn_sqlite__column_revnum(stmt, 12);
12695
12696 /* Provide the default status; we'll override as appropriate. */
12697 if (status)
12698 {
12699 if (presence == svn_wc__db_status_normal)
12700 *status = svn_wc__db_status_added;
12701 else
12702 *status = svn_wc__db_status_incomplete;
12703 }
12704
12705
12706 /* Calculate the op root local path components */
12707 op_root_relpath = svn_relpath_prefix(local_relpath, op_depth,
12708 scratch_pool);
12709 repos_prefix_path = svn_relpath_skip_ancestor(op_root_relpath,
12710 local_relpath);
12711
12712 if (op_root_relpath_p)
12713 *op_root_relpath_p = apr_pstrdup(result_pool, op_root_relpath);
12714
12715 /* ### This if-statement is quite redundant.
12716 * ### We're checking all these values again within the body anyway.
12717 * ### The body should be broken up appropriately and move into the
12718 * ### outer scope. */
12719 if (original_repos_relpath
12720 || original_repos_id
12721 || (original_revision
12722 && *original_revision == SVN_INVALID_REVNUM)
12723 || status
12724 || moved_from_relpath || moved_from_op_root_relpath)
12725 {
12726 if (local_relpath != op_root_relpath)
12727 /* requery to get the add/copy root */
12728 {
12729 SVN_ERR(svn_sqlite__reset(stmt));
12730
12731 SVN_ERR(svn_sqlite__bindf(stmt, "is",
12732 wcroot->wc_id, op_root_relpath));
12733 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12734
12735 if (!have_row)
12736 {
12737 /* Reset statement before returning */
12738 SVN_ERR(svn_sqlite__reset(stmt));
12739
12740 /* ### maybe we should return a usage error instead? */
12741 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12742 _("The node '%s' was not found."),
12743 path_for_error_message(wcroot,
12744 op_root_relpath,
12745 scratch_pool));
12746 }
12747
12748 if (original_revision
12749 && *original_revision == SVN_INVALID_REVNUM)
12750 *original_revision = svn_sqlite__column_revnum(stmt, 12);
12751 }
12752
12753 if (original_repos_relpath)
12754 *original_repos_relpath = svn_sqlite__column_text(stmt, 11,
12755 result_pool);
12756
12757 if (!svn_sqlite__column_is_null(stmt, 10)
12758 && (status
12759 || original_repos_id
12760 || moved_from_relpath || moved_from_op_root_relpath))
12761 /* If column 10 (original_repos_id) is NULL,
12762 this is a plain add, not a copy or a move */
12763 {
12764 svn_boolean_t moved_here;
12765 if (original_repos_id)
12766 *original_repos_id = svn_sqlite__column_int64(stmt, 10);
12767
12768 moved_here = svn_sqlite__column_boolean(stmt, 13 /* moved_here */);
12769 if (status)
12770 *status = moved_here ? svn_wc__db_status_moved_here
12771 : svn_wc__db_status_copied;
12772
12773 if (moved_here
12774 && (moved_from_relpath || moved_from_op_root_relpath))
12775 {
12776 svn_error_t *err;
12777
12778 err = get_moved_from_info(moved_from_relpath,
12779 moved_from_op_root_relpath,
12780 op_root_relpath,
12781 moved_from_op_depth,
12782 wcroot, local_relpath,
12783 result_pool,
12784 scratch_pool);
12785
12786 if (err)
12787 return svn_error_compose_create(
12788 err, svn_sqlite__reset(stmt));
12789 }
12790 }
12791 }
12792
12793
12794 /* ### This loop here is to skip up to the first node which is a BASE node,
12795 because base_get_info() doesn't accommodate the scenario that
12796 we're looking at here; we found the true op_root, which may be inside
12797 further changed trees. */
12798 if (repos_relpath || repos_id)
12799 {
12800 const char *base_relpath;
12801
12802 while (TRUE)
12803 {
12804 const char *tmp;
12805
12806 SVN_ERR(svn_sqlite__reset(stmt));
12807
12808 /* Pointing at op_depth, look at the parent */
12809 repos_prefix_path =
12810 svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
12811 repos_prefix_path,
12812 scratch_pool);
12813 op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
12814
12815
12816 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, op_root_relpath));
12817 SVN_ERR(svn_sqlite__step(&have_row, stmt));
12818
12819 if (! have_row)
12820 break;
12821
12822 op_depth = svn_sqlite__column_int(stmt, 0);
12823
12824 /* Skip to op_depth */
12825 tmp = op_root_relpath;
12826
12827 op_root_relpath = svn_relpath_prefix(op_root_relpath, op_depth,
12828 scratch_pool);
12829 repos_prefix_path = svn_relpath_join(
12830 svn_relpath_skip_ancestor(op_root_relpath, tmp),
12831 repos_prefix_path, scratch_pool);
12832 }
12833
12834 SVN_ERR(svn_sqlite__reset(stmt));
12835
12836 build_relpath = repos_prefix_path;
12837
12838 /* If we're here, then we have an added/copied/moved (start) node, and
12839 CURRENT_ABSPATH now points to a BASE node. Figure out the repository
12840 information for the current node, and use that to compute the start
12841 node's repository information. */
12842 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12843 &base_relpath, repos_id,
12844 NULL, NULL, NULL, NULL, NULL,
12845 NULL, NULL, NULL, NULL, NULL,
12846 wcroot, op_root_relpath,
12847 scratch_pool, scratch_pool));
12848
12849 if (repos_relpath)
12850 *repos_relpath = svn_relpath_join(base_relpath, build_relpath,
12851 result_pool);
12852 }
12853 else
12854 SVN_ERR(svn_sqlite__reset(stmt));
12855 }
12856 /* Postconditions */
12857 #ifdef SVN_DEBUG
12858 if (status)
12859 {
12860 SVN_ERR_ASSERT(*status == svn_wc__db_status_added
12861 || *status == svn_wc__db_status_copied
12862 || *status == svn_wc__db_status_incomplete
12863 || *status == svn_wc__db_status_moved_here);
12864 if (*status == svn_wc__db_status_added)
12865 {
12866 SVN_ERR_ASSERT(!original_repos_relpath
12867 || *original_repos_relpath == NULL);
12868 SVN_ERR_ASSERT(!original_revision
12869 || *original_revision == SVN_INVALID_REVNUM);
12870 SVN_ERR_ASSERT(!original_repos_id
12871 || *original_repos_id == INVALID_REPOS_ID);
12872 }
12873 /* An upgrade with a missing directory can leave INCOMPLETE working
12874 op-roots. See upgrade_tests.py 29: upgrade with missing replaced dir
12875 */
12876 else if (*status != svn_wc__db_status_incomplete)
12877 {
12878 SVN_ERR_ASSERT(!original_repos_relpath
12879 || *original_repos_relpath != NULL);
12880 SVN_ERR_ASSERT(!original_revision
12881 || *original_revision != SVN_INVALID_REVNUM);
12882 SVN_ERR_ASSERT(!original_repos_id
12883 || *original_repos_id != INVALID_REPOS_ID);
12884 }
12885 }
12886 SVN_ERR_ASSERT(!op_root_relpath_p || *op_root_relpath_p != NULL);
12887 #endif
12888
12889 return SVN_NO_ERROR;
12890 }
12891
12892 svn_error_t *
svn_wc__db_scan_addition_internal(svn_wc__db_status_t * status,const char ** op_root_relpath_p,const char ** repos_relpath,apr_int64_t * repos_id,const char ** original_repos_relpath,apr_int64_t * original_repos_id,svn_revnum_t * original_revision,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12893 svn_wc__db_scan_addition_internal(
12894 svn_wc__db_status_t *status,
12895 const char **op_root_relpath_p,
12896 const char **repos_relpath,
12897 apr_int64_t *repos_id,
12898 const char **original_repos_relpath,
12899 apr_int64_t *original_repos_id,
12900 svn_revnum_t *original_revision,
12901 svn_wc__db_wcroot_t *wcroot,
12902 const char *local_relpath,
12903 apr_pool_t *result_pool,
12904 apr_pool_t *scratch_pool)
12905 {
12906 return svn_error_trace(
12907 scan_addition(status, op_root_relpath_p, repos_relpath, repos_id,
12908 original_repos_relpath, original_repos_id,
12909 original_revision, NULL, NULL, NULL,
12910 wcroot, local_relpath, result_pool, scratch_pool));
12911 }
12912
12913 svn_error_t *
svn_wc__db_scan_addition(svn_wc__db_status_t * status,const char ** op_root_abspath,const char ** repos_relpath,const char ** repos_root_url,const char ** repos_uuid,const char ** original_repos_relpath,const char ** original_root_url,const char ** original_uuid,svn_revnum_t * original_revision,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12914 svn_wc__db_scan_addition(svn_wc__db_status_t *status,
12915 const char **op_root_abspath,
12916 const char **repos_relpath,
12917 const char **repos_root_url,
12918 const char **repos_uuid,
12919 const char **original_repos_relpath,
12920 const char **original_root_url,
12921 const char **original_uuid,
12922 svn_revnum_t *original_revision,
12923 svn_wc__db_t *db,
12924 const char *local_abspath,
12925 apr_pool_t *result_pool,
12926 apr_pool_t *scratch_pool)
12927 {
12928 svn_wc__db_wcroot_t *wcroot;
12929 const char *local_relpath;
12930 const char *op_root_relpath = NULL;
12931 apr_int64_t repos_id = INVALID_REPOS_ID;
12932 apr_int64_t original_repos_id = INVALID_REPOS_ID;
12933 apr_int64_t *repos_id_p
12934 = (repos_root_url || repos_uuid) ? &repos_id : NULL;
12935 apr_int64_t *original_repos_id_p
12936 = (original_root_url || original_uuid) ? &original_repos_id : NULL;
12937
12938 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12939
12940 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12941 local_abspath, scratch_pool, scratch_pool));
12942 VERIFY_USABLE_WCROOT(wcroot);
12943
12944 SVN_WC__DB_WITH_TXN4(
12945 scan_addition(status,
12946 op_root_abspath
12947 ? &op_root_relpath
12948 : NULL,
12949 repos_relpath, repos_id_p,
12950 original_repos_relpath, original_repos_id_p,
12951 original_revision,
12952 NULL, NULL, NULL,
12953 wcroot, local_relpath, result_pool, scratch_pool),
12954 svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot,
12955 repos_id, result_pool),
12956 svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
12957 wcroot, original_repos_id,
12958 result_pool),
12959 SVN_NO_ERROR,
12960 wcroot);
12961
12962 if (op_root_abspath)
12963 *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
12964 result_pool);
12965 /* REPOS_ID must be valid if requested; ORIGINAL_REPOS_ID need not be. */
12966 SVN_ERR_ASSERT(repos_id_p == NULL || repos_id != INVALID_REPOS_ID);
12967
12968 return SVN_NO_ERROR;
12969 }
12970
12971 svn_error_t *
svn_wc__db_scan_moved(const char ** moved_from_abspath,const char ** op_root_abspath,const char ** op_root_moved_from_abspath,const char ** moved_from_delete_abspath,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)12972 svn_wc__db_scan_moved(const char **moved_from_abspath,
12973 const char **op_root_abspath,
12974 const char **op_root_moved_from_abspath,
12975 const char **moved_from_delete_abspath,
12976 svn_wc__db_t *db,
12977 const char *local_abspath,
12978 apr_pool_t *result_pool,
12979 apr_pool_t *scratch_pool)
12980 {
12981 svn_wc__db_wcroot_t *wcroot;
12982 const char *local_relpath;
12983 svn_wc__db_status_t status;
12984 const char *op_root_relpath = NULL;
12985 const char *moved_from_relpath = NULL;
12986 const char *moved_from_op_root_relpath = NULL;
12987 int moved_from_op_depth = -1;
12988
12989 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12990
12991 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12992 local_abspath, scratch_pool, scratch_pool));
12993 VERIFY_USABLE_WCROOT(wcroot);
12994
12995 SVN_WC__DB_WITH_TXN(
12996 scan_addition(&status,
12997 op_root_abspath
12998 ? &op_root_relpath
12999 : NULL,
13000 NULL, NULL,
13001 NULL, NULL, NULL,
13002 moved_from_abspath
13003 ? &moved_from_relpath
13004 : NULL,
13005 (op_root_moved_from_abspath
13006 || moved_from_delete_abspath)
13007 ? &moved_from_op_root_relpath
13008 : NULL,
13009 moved_from_delete_abspath
13010 ? &moved_from_op_depth
13011 : NULL,
13012 wcroot, local_relpath, scratch_pool, scratch_pool),
13013 wcroot);
13014
13015 if (status != svn_wc__db_status_moved_here || !moved_from_relpath)
13016 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
13017 _("Path '%s' was not moved here"),
13018 path_for_error_message(wcroot, local_relpath,
13019 scratch_pool));
13020
13021 if (op_root_abspath)
13022 *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
13023 result_pool);
13024
13025 if (moved_from_abspath)
13026 *moved_from_abspath = svn_dirent_join(wcroot->abspath, moved_from_relpath,
13027 result_pool);
13028
13029 if (op_root_moved_from_abspath)
13030 *op_root_moved_from_abspath = svn_dirent_join(wcroot->abspath,
13031 moved_from_op_root_relpath,
13032 result_pool);
13033
13034 /* The deleted node is either where we moved from, or one of its ancestors */
13035 if (moved_from_delete_abspath)
13036 {
13037 const char *tmp = svn_relpath_prefix(moved_from_op_root_relpath,
13038 moved_from_op_depth, scratch_pool);
13039
13040 *moved_from_delete_abspath = svn_dirent_join(wcroot->abspath, tmp,
13041 scratch_pool);
13042 }
13043
13044 return SVN_NO_ERROR;
13045 }
13046
13047 /* ### Recursive helper for svn_wc__db_follow_moved_to()
13048 */
13049 static svn_error_t *
follow_moved_to(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int op_depth,apr_array_header_t ** moved_tos,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13050 follow_moved_to(svn_wc__db_wcroot_t *wcroot,
13051 const char *local_relpath,
13052 int op_depth,
13053 apr_array_header_t **moved_tos,
13054 apr_pool_t *result_pool,
13055 apr_pool_t *scratch_pool)
13056 {
13057 svn_sqlite__stmt_t *stmt;
13058 svn_boolean_t have_row;
13059 int shadowing_op_depth;
13060 const char *ancestor_relpath;
13061 const char *node_moved_to = NULL;
13062 int i;
13063
13064 /* Obtain the depth of the node directly shadowing local_relpath
13065 as it exists at OP_DEPTH, and perhaps moved to info */
13066 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13067 STMT_SELECT_OP_DEPTH_MOVED_TO));
13068 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
13069 op_depth));
13070 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13071 if (have_row)
13072 {
13073 shadowing_op_depth = svn_sqlite__column_int(stmt, 0);
13074 node_moved_to = svn_sqlite__column_text(stmt, 1, result_pool);
13075
13076 if (node_moved_to)
13077 {
13078 struct svn_wc__db_moved_to_t *moved_to;
13079
13080 moved_to = apr_palloc(result_pool, sizeof(*moved_to));
13081 moved_to->op_depth = shadowing_op_depth;
13082 moved_to->local_relpath = node_moved_to;
13083 APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
13084 }
13085 }
13086
13087 SVN_ERR(svn_sqlite__reset(stmt));
13088
13089 if (!have_row)
13090 {
13091 /* Node is not shadowed, so not moved */
13092 return SVN_NO_ERROR;
13093 }
13094 else if (node_moved_to)
13095 {
13096 /* Moved directly, so we have the final location */
13097 return SVN_NO_ERROR;
13098 }
13099 /* Need to handle being moved via an ancestor. */
13100 ancestor_relpath = local_relpath;
13101 for (i = relpath_depth(local_relpath); i > shadowing_op_depth; --i)
13102 {
13103 const char *ancestor_moved_to;
13104
13105 ancestor_relpath = svn_relpath_dirname(ancestor_relpath, scratch_pool);
13106
13107 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13108 STMT_SELECT_MOVED_TO));
13109 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, ancestor_relpath,
13110 shadowing_op_depth));
13111 SVN_ERR(svn_sqlite__step_row(stmt));
13112
13113 ancestor_moved_to = svn_sqlite__column_text(stmt, 0, scratch_pool);
13114 SVN_ERR(svn_sqlite__reset(stmt));
13115 if (ancestor_moved_to)
13116 {
13117 struct svn_wc__db_moved_to_t *moved_to;
13118
13119 node_moved_to
13120 = svn_relpath_join(ancestor_moved_to,
13121 svn_relpath_skip_ancestor(ancestor_relpath,
13122 local_relpath),
13123 result_pool);
13124
13125 moved_to = apr_palloc(result_pool, sizeof(*moved_to));
13126 moved_to->op_depth = shadowing_op_depth;
13127 moved_to->local_relpath = node_moved_to;
13128 APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
13129
13130 SVN_ERR(follow_moved_to(wcroot, node_moved_to,
13131 relpath_depth(ancestor_moved_to),
13132 moved_tos, result_pool, scratch_pool));
13133
13134 break;
13135 }
13136 }
13137
13138 return SVN_NO_ERROR;
13139 }
13140
13141 svn_error_t *
svn_wc__db_follow_moved_to(apr_array_header_t ** moved_tos,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13142 svn_wc__db_follow_moved_to(apr_array_header_t **moved_tos,
13143 svn_wc__db_t *db,
13144 const char *local_abspath,
13145 apr_pool_t *result_pool,
13146 apr_pool_t *scratch_pool)
13147 {
13148 svn_wc__db_wcroot_t *wcroot;
13149 const char *local_relpath;
13150
13151 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13152
13153 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13154 local_abspath, scratch_pool, scratch_pool));
13155 VERIFY_USABLE_WCROOT(wcroot);
13156
13157 *moved_tos = apr_array_make(result_pool, 0,
13158 sizeof(struct svn_wc__db_moved_to_t *));
13159
13160 /* ### Wrap in a transaction */
13161 SVN_WC__DB_WITH_TXN(follow_moved_to(wcroot, local_relpath, 0, moved_tos,
13162 result_pool, scratch_pool),
13163 wcroot);
13164
13165 /* ### Convert moved_to to abspath */
13166
13167 return SVN_NO_ERROR;
13168 }
13169
13170 svn_error_t *
svn_wc__db_scan_moved_to_internal(const char ** move_src_relpath,const char ** move_dst_relpath,const char ** delete_relpath,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int op_depth,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13171 svn_wc__db_scan_moved_to_internal(const char **move_src_relpath,
13172 const char **move_dst_relpath,
13173 const char **delete_relpath,
13174 svn_wc__db_wcroot_t *wcroot,
13175 const char *local_relpath,
13176 int op_depth,
13177 apr_pool_t *result_pool,
13178 apr_pool_t *scratch_pool)
13179 {
13180 svn_sqlite__stmt_t *stmt;
13181 svn_boolean_t have_row;
13182 int delete_op_depth;
13183 const char *relpath = local_relpath;
13184 const char *dst_relpath;
13185
13186 SVN_ERR_ASSERT(local_relpath[0]); /* Not valid on the WC root */
13187
13188 if (move_src_relpath)
13189 *move_src_relpath = NULL;
13190 if (move_dst_relpath)
13191 *move_dst_relpath = NULL;
13192 if (delete_relpath)
13193 *delete_relpath = NULL;
13194
13195 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13196 STMT_SELECT_OP_DEPTH_MOVED_TO));
13197 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath, op_depth));
13198
13199 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13200
13201 if (!have_row)
13202 {
13203 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
13204 svn_sqlite__reset(stmt),
13205 _("Node '%s' is not shadowed"),
13206 path_for_error_message(wcroot, local_relpath,
13207 scratch_pool));
13208 }
13209
13210 delete_op_depth = svn_sqlite__column_int(stmt, 0);
13211 dst_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
13212
13213 SVN_ERR(svn_sqlite__reset(stmt));
13214
13215 while (!dst_relpath && have_row)
13216 {
13217 relpath = svn_relpath_dirname(relpath, scratch_pool);
13218
13219 if (relpath_depth(relpath) < delete_op_depth)
13220 break;
13221
13222 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13223 STMT_SELECT_DEPTH_NODE));
13224 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath,
13225 delete_op_depth));
13226
13227 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13228
13229 if (have_row)
13230 dst_relpath = svn_sqlite__column_text(stmt, 13, scratch_pool);
13231
13232 SVN_ERR(svn_sqlite__reset(stmt));
13233 }
13234
13235 if (dst_relpath)
13236 {
13237 if (move_src_relpath)
13238 *move_src_relpath = apr_pstrdup(result_pool, relpath);
13239
13240 if (move_dst_relpath)
13241 *move_dst_relpath = apr_pstrdup(result_pool, dst_relpath);
13242
13243 if (delete_relpath)
13244 *delete_relpath = svn_relpath_prefix(local_relpath, delete_op_depth,
13245 result_pool);
13246 }
13247
13248 return SVN_NO_ERROR;
13249 }
13250
13251 /* Public (within libsvn_wc) absolute path version of
13252 svn_wc__db_op_depth_moved_to with the op-depth hard-coded to
13253 BASE. */
13254 svn_error_t *
svn_wc__db_base_moved_to(const char ** move_dst_abspath,const char ** move_dst_op_root_abspath,const char ** move_src_root_abspath,const char ** delete_abspath,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13255 svn_wc__db_base_moved_to(const char **move_dst_abspath,
13256 const char **move_dst_op_root_abspath,
13257 const char **move_src_root_abspath,
13258 const char **delete_abspath,
13259 svn_wc__db_t *db,
13260 const char *local_abspath,
13261 apr_pool_t *result_pool,
13262 apr_pool_t *scratch_pool)
13263 {
13264 svn_wc__db_wcroot_t *wcroot;
13265 const char *local_relpath;
13266 const char *dst_root_relpath;
13267 const char *src_root_relpath, *delete_relpath;
13268
13269 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13270
13271
13272 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13273 local_abspath, scratch_pool, scratch_pool));
13274 VERIFY_USABLE_WCROOT(wcroot);
13275
13276 SVN_WC__DB_WITH_TXN(svn_wc__db_scan_moved_to_internal(&src_root_relpath,
13277 &dst_root_relpath,
13278 &delete_relpath,
13279 wcroot, local_relpath,
13280 0 /* BASE */,
13281 scratch_pool,
13282 scratch_pool),
13283 wcroot);
13284
13285 if (move_dst_abspath)
13286 *move_dst_abspath =
13287 dst_root_relpath
13288 ? svn_dirent_join(wcroot->abspath,
13289 svn_dirent_join(
13290 dst_root_relpath,
13291 svn_relpath_skip_ancestor(src_root_relpath,
13292 local_relpath),
13293 scratch_pool),
13294 result_pool)
13295 : NULL;
13296
13297 if (move_dst_op_root_abspath)
13298 *move_dst_op_root_abspath =
13299 dst_root_relpath
13300 ? svn_dirent_join(wcroot->abspath, dst_root_relpath, result_pool)
13301 : NULL;
13302
13303 if (move_src_root_abspath)
13304 *move_src_root_abspath =
13305 src_root_relpath
13306 ? svn_dirent_join(wcroot->abspath, src_root_relpath, result_pool)
13307 : NULL;
13308
13309 if (delete_abspath)
13310 *delete_abspath =
13311 delete_relpath
13312 ? svn_dirent_join(wcroot->abspath, delete_relpath, result_pool)
13313 : NULL;
13314
13315 return SVN_NO_ERROR;
13316 }
13317
13318 svn_error_t *
svn_wc__db_upgrade_begin(svn_sqlite__db_t ** sdb,apr_int64_t * repos_id,apr_int64_t * wc_id,svn_wc__db_t * wc_db,const char * dir_abspath,const char * repos_root_url,const char * repos_uuid,apr_pool_t * scratch_pool)13319 svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb,
13320 apr_int64_t *repos_id,
13321 apr_int64_t *wc_id,
13322 svn_wc__db_t *wc_db,
13323 const char *dir_abspath,
13324 const char *repos_root_url,
13325 const char *repos_uuid,
13326 apr_pool_t *scratch_pool)
13327 {
13328 svn_wc__db_wcroot_t *wcroot;
13329
13330 /* Upgrade is inherently exclusive so specify exclusive locking. */
13331 SVN_ERR(create_db(sdb, repos_id, wc_id, dir_abspath,
13332 repos_root_url, repos_uuid,
13333 SDB_FILE,
13334 NULL, SVN_INVALID_REVNUM, svn_depth_unknown,
13335 TRUE /* exclusive */,
13336 0 /* timeout */,
13337 wc_db->state_pool, scratch_pool));
13338
13339 SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
13340 apr_pstrdup(wc_db->state_pool,
13341 dir_abspath),
13342 *sdb, *wc_id, FORMAT_FROM_SDB,
13343 FALSE /* auto-upgrade */,
13344 wc_db->state_pool, scratch_pool));
13345
13346 /* The WCROOT is complete. Stash it into DB. */
13347 svn_hash_sets(wc_db->dir_data, wcroot->abspath, wcroot);
13348
13349 return SVN_NO_ERROR;
13350 }
13351
13352 svn_error_t *
svn_wc__db_upgrade_insert_external(svn_wc__db_t * db,const char * local_abspath,svn_node_kind_t kind,const char * parent_abspath,const char * def_local_abspath,const char * repos_relpath,const char * repos_root_url,const char * repos_uuid,svn_revnum_t def_peg_revision,svn_revnum_t def_revision,apr_pool_t * scratch_pool)13353 svn_wc__db_upgrade_insert_external(svn_wc__db_t *db,
13354 const char *local_abspath,
13355 svn_node_kind_t kind,
13356 const char *parent_abspath,
13357 const char *def_local_abspath,
13358 const char *repos_relpath,
13359 const char *repos_root_url,
13360 const char *repos_uuid,
13361 svn_revnum_t def_peg_revision,
13362 svn_revnum_t def_revision,
13363 apr_pool_t *scratch_pool)
13364 {
13365 svn_wc__db_wcroot_t *wcroot;
13366 const char *def_local_relpath;
13367 svn_sqlite__stmt_t *stmt;
13368 svn_boolean_t have_row;
13369 apr_int64_t repos_id;
13370
13371 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13372
13373 /* We know only of DEF_LOCAL_ABSPATH that it definitely belongs to "this"
13374 * WC, i.e. where the svn:externals prop is set. The external target path
13375 * itself may be "hidden behind" other working copies. */
13376 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &def_local_relpath,
13377 db, def_local_abspath,
13378 scratch_pool, scratch_pool));
13379 VERIFY_USABLE_WCROOT(wcroot);
13380
13381
13382 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13383 STMT_SELECT_REPOSITORY));
13384 SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
13385 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13386
13387 if (have_row)
13388 repos_id = svn_sqlite__column_int64(stmt, 0);
13389 SVN_ERR(svn_sqlite__reset(stmt));
13390
13391 if (!have_row)
13392 {
13393 /* Need to set up a new repository row. */
13394 SVN_ERR(create_repos_id(&repos_id, repos_root_url, repos_uuid,
13395 wcroot->sdb, scratch_pool));
13396 }
13397
13398 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13399 STMT_INSERT_EXTERNAL));
13400
13401 /* wc_id, local_relpath, parent_relpath, presence, kind, def_local_relpath,
13402 * repos_id, def_repos_relpath, def_operational_revision, def_revision */
13403 SVN_ERR(svn_sqlite__bindf(stmt, "issstsis",
13404 wcroot->wc_id,
13405 svn_dirent_skip_ancestor(wcroot->abspath,
13406 local_abspath),
13407 svn_dirent_skip_ancestor(wcroot->abspath,
13408 parent_abspath),
13409 "normal",
13410 kind_map, kind,
13411 def_local_relpath,
13412 repos_id,
13413 repos_relpath));
13414
13415 if (SVN_IS_VALID_REVNUM(def_peg_revision))
13416 SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, def_peg_revision));
13417
13418 if (SVN_IS_VALID_REVNUM(def_revision))
13419 SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, def_revision));
13420
13421 SVN_ERR(svn_sqlite__insert(NULL, stmt));
13422
13423 return SVN_NO_ERROR;
13424 }
13425
13426 svn_error_t *
svn_wc__db_wq_add_internal(svn_wc__db_wcroot_t * wcroot,const svn_skel_t * work_item,apr_pool_t * scratch_pool)13427 svn_wc__db_wq_add_internal(svn_wc__db_wcroot_t *wcroot,
13428 const svn_skel_t *work_item,
13429 apr_pool_t *scratch_pool)
13430 {
13431 /* Add the work item(s) to the WORK_QUEUE. */
13432 return svn_error_trace(add_work_items(wcroot->sdb, work_item,
13433 scratch_pool));
13434 }
13435
13436 svn_error_t *
svn_wc__db_wq_add(svn_wc__db_t * db,const char * wri_abspath,const svn_skel_t * work_item,apr_pool_t * scratch_pool)13437 svn_wc__db_wq_add(svn_wc__db_t *db,
13438 const char *wri_abspath,
13439 const svn_skel_t *work_item,
13440 apr_pool_t *scratch_pool)
13441 {
13442 svn_wc__db_wcroot_t *wcroot;
13443 const char *local_relpath;
13444
13445 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13446
13447 /* Quick exit, if there are no work items to queue up. */
13448 if (work_item == NULL)
13449 return SVN_NO_ERROR;
13450
13451 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13452 wri_abspath, scratch_pool, scratch_pool));
13453 VERIFY_USABLE_WCROOT(wcroot);
13454
13455 /* Add the work item(s) to the WORK_QUEUE. */
13456 return svn_error_trace(add_work_items(wcroot->sdb, work_item,
13457 scratch_pool));
13458 }
13459
13460 /* The body of svn_wc__db_wq_fetch_next().
13461 */
13462 static svn_error_t *
wq_fetch_next(apr_uint64_t * id,svn_skel_t ** work_item,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_uint64_t completed_id,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13463 wq_fetch_next(apr_uint64_t *id,
13464 svn_skel_t **work_item,
13465 svn_wc__db_wcroot_t *wcroot,
13466 const char *local_relpath,
13467 apr_uint64_t completed_id,
13468 apr_pool_t *result_pool,
13469 apr_pool_t *scratch_pool)
13470 {
13471 svn_sqlite__stmt_t *stmt;
13472 svn_boolean_t have_row;
13473
13474 if (completed_id != 0)
13475 {
13476 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13477 STMT_DELETE_WORK_ITEM));
13478 SVN_ERR(svn_sqlite__bind_int64(stmt, 1, completed_id));
13479
13480 SVN_ERR(svn_sqlite__step_done(stmt));
13481 }
13482
13483 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13484 STMT_SELECT_WORK_ITEM));
13485 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13486
13487 if (!have_row)
13488 {
13489 *id = 0;
13490 *work_item = NULL;
13491 }
13492 else
13493 {
13494 apr_size_t len;
13495 const void *val;
13496
13497 *id = svn_sqlite__column_int64(stmt, 0);
13498
13499 val = svn_sqlite__column_blob(stmt, 1, &len, result_pool);
13500
13501 *work_item = svn_skel__parse(val, len, result_pool);
13502 }
13503
13504 return svn_error_trace(svn_sqlite__reset(stmt));
13505 }
13506
13507 svn_error_t *
svn_wc__db_wq_fetch_next(apr_uint64_t * id,svn_skel_t ** work_item,svn_wc__db_t * db,const char * wri_abspath,apr_uint64_t completed_id,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13508 svn_wc__db_wq_fetch_next(apr_uint64_t *id,
13509 svn_skel_t **work_item,
13510 svn_wc__db_t *db,
13511 const char *wri_abspath,
13512 apr_uint64_t completed_id,
13513 apr_pool_t *result_pool,
13514 apr_pool_t *scratch_pool)
13515 {
13516 svn_wc__db_wcroot_t *wcroot;
13517 const char *local_relpath;
13518
13519 SVN_ERR_ASSERT(id != NULL);
13520 SVN_ERR_ASSERT(work_item != NULL);
13521 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13522
13523 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13524 wri_abspath, scratch_pool, scratch_pool));
13525 VERIFY_USABLE_WCROOT(wcroot);
13526
13527 SVN_WC__DB_WITH_TXN(
13528 wq_fetch_next(id, work_item,
13529 wcroot, local_relpath, completed_id,
13530 result_pool, scratch_pool),
13531 wcroot);
13532
13533 return SVN_NO_ERROR;
13534 }
13535
13536 /* Records timestamp and date for one or more files in wcroot */
13537 static svn_error_t *
wq_record(svn_wc__db_wcroot_t * wcroot,apr_hash_t * record_map,apr_pool_t * scratch_pool)13538 wq_record(svn_wc__db_wcroot_t *wcroot,
13539 apr_hash_t *record_map,
13540 apr_pool_t *scratch_pool)
13541 {
13542 apr_hash_index_t *hi;
13543 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
13544
13545 for (hi = apr_hash_first(scratch_pool, record_map); hi;
13546 hi = apr_hash_next(hi))
13547 {
13548 const char *local_abspath = apr_hash_this_key(hi);
13549 const svn_io_dirent2_t *dirent = apr_hash_this_val(hi);
13550 const char *local_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
13551 local_abspath);
13552
13553 svn_pool_clear(iterpool);
13554
13555 if (! local_relpath)
13556 continue;
13557
13558 SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
13559 dirent->filesize, dirent->mtime,
13560 iterpool));
13561 }
13562
13563 svn_pool_destroy(iterpool);
13564 return SVN_NO_ERROR;
13565 }
13566
13567 svn_error_t *
svn_wc__db_wq_record_and_fetch_next(apr_uint64_t * id,svn_skel_t ** work_item,svn_wc__db_t * db,const char * wri_abspath,apr_uint64_t completed_id,apr_hash_t * record_map,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13568 svn_wc__db_wq_record_and_fetch_next(apr_uint64_t *id,
13569 svn_skel_t **work_item,
13570 svn_wc__db_t *db,
13571 const char *wri_abspath,
13572 apr_uint64_t completed_id,
13573 apr_hash_t *record_map,
13574 apr_pool_t *result_pool,
13575 apr_pool_t *scratch_pool)
13576 {
13577 svn_wc__db_wcroot_t *wcroot;
13578 const char *local_relpath;
13579
13580 SVN_ERR_ASSERT(id != NULL);
13581 SVN_ERR_ASSERT(work_item != NULL);
13582 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13583
13584 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13585 wri_abspath, scratch_pool, scratch_pool));
13586 VERIFY_USABLE_WCROOT(wcroot);
13587
13588 SVN_WC__DB_WITH_TXN(
13589 svn_error_compose_create(
13590 wq_fetch_next(id, work_item,
13591 wcroot, local_relpath, completed_id,
13592 result_pool, scratch_pool),
13593 wq_record(wcroot, record_map, scratch_pool)),
13594 wcroot);
13595
13596 return SVN_NO_ERROR;
13597 }
13598
13599
13600
13601 /* ### temporary API. remove before release. */
13602 svn_error_t *
svn_wc__db_temp_get_format(int * format,svn_wc__db_t * db,const char * local_dir_abspath,apr_pool_t * scratch_pool)13603 svn_wc__db_temp_get_format(int *format,
13604 svn_wc__db_t *db,
13605 const char *local_dir_abspath,
13606 apr_pool_t *scratch_pool)
13607 {
13608 svn_wc__db_wcroot_t *wcroot;
13609 const char *local_relpath;
13610 svn_error_t *err;
13611
13612 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13613 /* ### assert that we were passed a directory? */
13614
13615 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13616 local_dir_abspath, scratch_pool, scratch_pool);
13617
13618 /* If we hit an error examining this directory, then declare this
13619 directory to not be a working copy. */
13620 if (err)
13621 {
13622 if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
13623 return svn_error_trace(err);
13624 svn_error_clear(err);
13625
13626 /* Remap the returned error. */
13627 *format = 0;
13628 return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
13629 _("'%s' is not a working copy"),
13630 svn_dirent_local_style(local_dir_abspath,
13631 scratch_pool));
13632 }
13633
13634 SVN_ERR_ASSERT(wcroot != NULL);
13635 SVN_ERR_ASSERT(wcroot->format >= 1);
13636
13637 *format = wcroot->format;
13638
13639 return SVN_NO_ERROR;
13640 }
13641
13642 /* ### temporary API. remove before release. */
13643 svn_wc_adm_access_t *
svn_wc__db_temp_get_access(svn_wc__db_t * db,const char * local_dir_abspath,apr_pool_t * scratch_pool)13644 svn_wc__db_temp_get_access(svn_wc__db_t *db,
13645 const char *local_dir_abspath,
13646 apr_pool_t *scratch_pool)
13647 {
13648 const char *local_relpath;
13649 svn_wc__db_wcroot_t *wcroot;
13650 svn_error_t *err;
13651
13652 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13653
13654 /* ### we really need to assert that we were passed a directory. sometimes
13655 ### adm_retrieve_internal is asked about a file, and then it asks us
13656 ### for an access baton for it. we should definitely return NULL, but
13657 ### ideally: the caller would never ask us about a non-directory. */
13658
13659 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13660 db, local_dir_abspath, scratch_pool, scratch_pool);
13661 if (err)
13662 {
13663 svn_error_clear(err);
13664 return NULL;
13665 }
13666
13667 if (!wcroot)
13668 return NULL;
13669
13670 return svn_hash_gets(wcroot->access_cache, local_dir_abspath);
13671 }
13672
13673
13674 /* ### temporary API. remove before release. */
13675 void
svn_wc__db_temp_set_access(svn_wc__db_t * db,const char * local_dir_abspath,svn_wc_adm_access_t * adm_access,apr_pool_t * scratch_pool)13676 svn_wc__db_temp_set_access(svn_wc__db_t *db,
13677 const char *local_dir_abspath,
13678 svn_wc_adm_access_t *adm_access,
13679 apr_pool_t *scratch_pool)
13680 {
13681 const char *local_relpath;
13682 svn_wc__db_wcroot_t *wcroot;
13683 svn_error_t *err;
13684
13685 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13686 /* ### assert that we were passed a directory? */
13687
13688 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13689 db, local_dir_abspath, scratch_pool, scratch_pool);
13690 if (err)
13691 {
13692 /* We don't even have a wcroot, so just bail. */
13693 svn_error_clear(err);
13694 return;
13695 }
13696
13697 /* Better not override something already there. */
13698 SVN_ERR_ASSERT_NO_RETURN(
13699 svn_hash_gets(wcroot->access_cache, local_dir_abspath) == NULL
13700 );
13701 svn_hash_sets(wcroot->access_cache, local_dir_abspath, adm_access);
13702 }
13703
13704
13705 /* ### temporary API. remove before release. */
13706 svn_error_t *
svn_wc__db_temp_close_access(svn_wc__db_t * db,const char * local_dir_abspath,svn_wc_adm_access_t * adm_access,apr_pool_t * scratch_pool)13707 svn_wc__db_temp_close_access(svn_wc__db_t *db,
13708 const char *local_dir_abspath,
13709 svn_wc_adm_access_t *adm_access,
13710 apr_pool_t *scratch_pool)
13711 {
13712 const char *local_relpath;
13713 svn_wc__db_wcroot_t *wcroot;
13714
13715 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13716 /* ### assert that we were passed a directory? */
13717
13718 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13719 local_dir_abspath, scratch_pool, scratch_pool));
13720 svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13721
13722 return SVN_NO_ERROR;
13723 }
13724
13725
13726 /* ### temporary API. remove before release. */
13727 void
svn_wc__db_temp_clear_access(svn_wc__db_t * db,const char * local_dir_abspath,apr_pool_t * scratch_pool)13728 svn_wc__db_temp_clear_access(svn_wc__db_t *db,
13729 const char *local_dir_abspath,
13730 apr_pool_t *scratch_pool)
13731 {
13732 const char *local_relpath;
13733 svn_wc__db_wcroot_t *wcroot;
13734 svn_error_t *err;
13735
13736 SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13737 /* ### assert that we were passed a directory? */
13738
13739 err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13740 db, local_dir_abspath, scratch_pool, scratch_pool);
13741 if (err)
13742 {
13743 svn_error_clear(err);
13744 return;
13745 }
13746
13747 svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13748 }
13749
13750
13751 apr_hash_t *
svn_wc__db_temp_get_all_access(svn_wc__db_t * db,apr_pool_t * result_pool)13752 svn_wc__db_temp_get_all_access(svn_wc__db_t *db,
13753 apr_pool_t *result_pool)
13754 {
13755 apr_hash_t *result = apr_hash_make(result_pool);
13756 apr_hash_index_t *hi;
13757
13758 for (hi = apr_hash_first(result_pool, db->dir_data);
13759 hi;
13760 hi = apr_hash_next(hi))
13761 {
13762 const svn_wc__db_wcroot_t *wcroot = apr_hash_this_val(hi);
13763
13764 /* This is highly redundant, 'cause the same WCROOT will appear many
13765 times in dir_data. */
13766 result = apr_hash_overlay(result_pool, result, wcroot->access_cache);
13767 }
13768
13769 return result;
13770 }
13771
13772
13773 svn_error_t *
svn_wc__db_temp_borrow_sdb(svn_sqlite__db_t ** sdb,svn_wc__db_t * db,const char * local_dir_abspath,apr_pool_t * scratch_pool)13774 svn_wc__db_temp_borrow_sdb(svn_sqlite__db_t **sdb,
13775 svn_wc__db_t *db,
13776 const char *local_dir_abspath,
13777 apr_pool_t *scratch_pool)
13778 {
13779 svn_wc__db_wcroot_t *wcroot;
13780 const char *local_relpath;
13781
13782 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13783
13784 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13785 local_dir_abspath, scratch_pool, scratch_pool));
13786 VERIFY_USABLE_WCROOT(wcroot);
13787
13788 *sdb = wcroot->sdb;
13789
13790 return SVN_NO_ERROR;
13791 }
13792
13793
13794 svn_error_t *
svn_wc__db_read_conflict_victims(const apr_array_header_t ** victims,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13795 svn_wc__db_read_conflict_victims(const apr_array_header_t **victims,
13796 svn_wc__db_t *db,
13797 const char *local_abspath,
13798 apr_pool_t *result_pool,
13799 apr_pool_t *scratch_pool)
13800 {
13801 svn_wc__db_wcroot_t *wcroot;
13802 const char *local_relpath;
13803 svn_sqlite__stmt_t *stmt;
13804 svn_boolean_t have_row;
13805 apr_array_header_t *new_victims;
13806
13807 /* The parent should be a working copy directory. */
13808 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13809 local_abspath, scratch_pool, scratch_pool));
13810 VERIFY_USABLE_WCROOT(wcroot);
13811
13812 /* ### This will be much easier once we have all conflicts in one
13813 field of actual*/
13814
13815 /* Look for text, tree and property conflicts in ACTUAL */
13816 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13817 STMT_SELECT_CONFLICT_VICTIMS));
13818 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13819
13820 new_victims = apr_array_make(result_pool, 0, sizeof(const char *));
13821
13822 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13823 while (have_row)
13824 {
13825 const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
13826
13827 APR_ARRAY_PUSH(new_victims, const char *) =
13828 svn_relpath_basename(child_relpath, result_pool);
13829
13830 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13831 }
13832
13833 SVN_ERR(svn_sqlite__reset(stmt));
13834
13835 *victims = new_victims;
13836 return SVN_NO_ERROR;
13837 }
13838
13839 /* The body of svn_wc__db_get_conflict_marker_files().
13840 */
13841 static svn_error_t *
get_conflict_marker_files(apr_hash_t ** marker_files_p,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_wc__db_t * db,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13842 get_conflict_marker_files(apr_hash_t **marker_files_p,
13843 svn_wc__db_wcroot_t *wcroot,
13844 const char *local_relpath,
13845 svn_wc__db_t *db,
13846 apr_pool_t *result_pool,
13847 apr_pool_t *scratch_pool)
13848 {
13849 svn_sqlite__stmt_t *stmt;
13850 svn_boolean_t have_row;
13851 apr_hash_t *marker_files = apr_hash_make(result_pool);
13852
13853 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13854 STMT_SELECT_ACTUAL_NODE));
13855 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13856 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13857
13858 if (have_row && !svn_sqlite__column_is_null(stmt, 2))
13859 {
13860 apr_size_t len;
13861 const void *data = svn_sqlite__column_blob(stmt, 2, &len, NULL);
13862 svn_skel_t *conflicts;
13863 const apr_array_header_t *markers;
13864 int i;
13865
13866 conflicts = svn_skel__parse(data, len, scratch_pool);
13867
13868 /* ### ADD markers to *marker_files */
13869 SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13870 conflicts,
13871 result_pool, scratch_pool));
13872
13873 for (i = 0; markers && (i < markers->nelts); i++)
13874 {
13875 const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13876
13877 svn_hash_sets(marker_files, marker_abspath, "");
13878 }
13879 }
13880 SVN_ERR(svn_sqlite__reset(stmt));
13881
13882 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13883 STMT_SELECT_CONFLICT_VICTIMS));
13884 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13885 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13886
13887 while (have_row)
13888 {
13889 apr_size_t len;
13890 const void *data = svn_sqlite__column_blob(stmt, 1, &len, NULL);
13891
13892 const apr_array_header_t *markers;
13893 int i;
13894
13895 if (data)
13896 {
13897 svn_skel_t *conflicts;
13898 conflicts = svn_skel__parse(data, len, scratch_pool);
13899
13900 SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13901 conflicts,
13902 result_pool, scratch_pool));
13903
13904 for (i = 0; markers && (i < markers->nelts); i++)
13905 {
13906 const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13907
13908 svn_hash_sets(marker_files, marker_abspath, "");
13909 }
13910 }
13911
13912 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13913 }
13914
13915 if (apr_hash_count(marker_files))
13916 *marker_files_p = marker_files;
13917 else
13918 *marker_files_p = NULL;
13919
13920 return svn_error_trace(svn_sqlite__reset(stmt));
13921 }
13922
13923 svn_error_t *
svn_wc__db_get_conflict_marker_files(apr_hash_t ** marker_files,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13924 svn_wc__db_get_conflict_marker_files(apr_hash_t **marker_files,
13925 svn_wc__db_t *db,
13926 const char *local_abspath,
13927 apr_pool_t *result_pool,
13928 apr_pool_t *scratch_pool)
13929 {
13930 svn_wc__db_wcroot_t *wcroot;
13931 const char *local_relpath;
13932
13933 /* The parent should be a working copy directory. */
13934 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13935 local_abspath, scratch_pool, scratch_pool));
13936 VERIFY_USABLE_WCROOT(wcroot);
13937
13938 SVN_WC__DB_WITH_TXN(
13939 get_conflict_marker_files(marker_files, wcroot, local_relpath, db,
13940 result_pool, scratch_pool),
13941 wcroot);
13942
13943 return SVN_NO_ERROR;
13944 }
13945
13946
13947 svn_error_t *
svn_wc__db_read_conflict(svn_skel_t ** conflict,svn_node_kind_t * kind,apr_hash_t ** props,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13948 svn_wc__db_read_conflict(svn_skel_t **conflict,
13949 svn_node_kind_t *kind,
13950 apr_hash_t **props,
13951 svn_wc__db_t *db,
13952 const char *local_abspath,
13953 apr_pool_t *result_pool,
13954 apr_pool_t *scratch_pool)
13955 {
13956 svn_wc__db_wcroot_t *wcroot;
13957 const char *local_relpath;
13958
13959 /* The parent should be a working copy directory. */
13960 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13961 local_abspath, scratch_pool, scratch_pool));
13962 VERIFY_USABLE_WCROOT(wcroot);
13963
13964 return svn_error_trace(svn_wc__db_read_conflict_internal(conflict, kind, props,
13965 wcroot, local_relpath,
13966 result_pool,
13967 scratch_pool));
13968 }
13969
13970 svn_error_t *
svn_wc__db_read_conflict_internal(svn_skel_t ** conflict,svn_node_kind_t * kind,apr_hash_t ** props,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)13971 svn_wc__db_read_conflict_internal(svn_skel_t **conflict,
13972 svn_node_kind_t *kind,
13973 apr_hash_t **props,
13974 svn_wc__db_wcroot_t *wcroot,
13975 const char *local_relpath,
13976 apr_pool_t *result_pool,
13977 apr_pool_t *scratch_pool)
13978 {
13979 svn_sqlite__stmt_t *stmt;
13980 svn_boolean_t have_row;
13981
13982 if (kind)
13983 *kind = svn_node_none;
13984 if (props)
13985 *props = NULL;
13986
13987 /* Check if we have a conflict in ACTUAL */
13988 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13989 STMT_SELECT_ACTUAL_NODE));
13990 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13991
13992 SVN_ERR(svn_sqlite__step(&have_row, stmt));
13993
13994 if (have_row)
13995 {
13996 apr_size_t cfl_len;
13997 const void *cfl_data;
13998
13999 /* svn_skel__parse doesn't copy data, so store in result_pool */
14000 cfl_data = svn_sqlite__column_blob(stmt, 2, &cfl_len, result_pool);
14001
14002 if (cfl_data)
14003 *conflict = svn_skel__parse(cfl_data, cfl_len, result_pool);
14004 else
14005 *conflict = NULL;
14006
14007 if (props)
14008 {
14009 svn_error_t *err;
14010
14011 err = svn_error_trace(svn_sqlite__column_properties(props, stmt, 1,
14012 result_pool,
14013 scratch_pool));
14014
14015 if (err)
14016 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
14017 }
14018 }
14019 else
14020 *conflict = NULL;
14021
14022 SVN_ERR(svn_sqlite__reset(stmt));
14023
14024 if (!have_row || kind || (props && !*props))
14025 {
14026 svn_error_t *err = NULL;
14027 svn_boolean_t have_info = FALSE;
14028
14029 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14030 STMT_SELECT_NODE_INFO));
14031
14032 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
14033 local_relpath));
14034
14035 SVN_ERR(svn_sqlite__step(&have_info, stmt));
14036
14037 if (have_info)
14038 {
14039 if (kind)
14040 {
14041 svn_wc__db_status_t status;
14042 int op_depth = svn_sqlite__column_int(stmt, 0);
14043
14044 status = svn_sqlite__column_token(stmt, 3, presence_map);
14045
14046 if (op_depth > 0)
14047 err = convert_to_working_status(&status, status);
14048
14049 if (!err && (status == svn_wc__db_status_normal
14050 || status == svn_wc__db_status_added
14051 || status == svn_wc__db_status_deleted
14052 || status == svn_wc__db_status_incomplete))
14053 {
14054 *kind = svn_sqlite__column_token(stmt, 4, kind_map);
14055 }
14056 }
14057
14058 /* Need props, and no props in ACTUAL? */
14059 if (!err && (props && !*props))
14060 {
14061 err = svn_sqlite__column_properties(props, stmt, 14,
14062 result_pool, scratch_pool);
14063 }
14064 }
14065
14066 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14067
14068 if (!have_row && !have_info)
14069 {
14070 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14071 _("The node '%s' was not found."),
14072 path_for_error_message(wcroot,
14073 local_relpath,
14074 scratch_pool));
14075 }
14076 }
14077
14078 return SVN_NO_ERROR;
14079 }
14080
14081
14082 svn_error_t *
svn_wc__db_read_kind(svn_node_kind_t * kind,svn_wc__db_t * db,const char * local_abspath,svn_boolean_t allow_missing,svn_boolean_t show_deleted,svn_boolean_t show_hidden,apr_pool_t * scratch_pool)14083 svn_wc__db_read_kind(svn_node_kind_t *kind,
14084 svn_wc__db_t *db,
14085 const char *local_abspath,
14086 svn_boolean_t allow_missing,
14087 svn_boolean_t show_deleted,
14088 svn_boolean_t show_hidden,
14089 apr_pool_t *scratch_pool)
14090 {
14091 svn_wc__db_wcroot_t *wcroot;
14092 const char *local_relpath;
14093 svn_sqlite__stmt_t *stmt_info;
14094 svn_boolean_t have_info;
14095 svn_wc__db_status_t status;
14096
14097 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14098
14099 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14100 local_abspath, scratch_pool, scratch_pool));
14101 VERIFY_USABLE_WCROOT(wcroot);
14102
14103 SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
14104 STMT_SELECT_NODE_INFO));
14105 SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
14106 SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
14107
14108 if (!have_info)
14109 {
14110 if (allow_missing)
14111 {
14112 *kind = svn_node_unknown;
14113 SVN_ERR(svn_sqlite__reset(stmt_info));
14114 return SVN_NO_ERROR;
14115 }
14116 else
14117 {
14118 SVN_ERR(svn_sqlite__reset(stmt_info));
14119 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14120 _("The node '%s' was not found."),
14121 path_for_error_message(wcroot,
14122 local_relpath,
14123 scratch_pool));
14124 }
14125 }
14126
14127 status = svn_sqlite__column_token(stmt_info, 3, presence_map);
14128
14129 if (show_deleted && status == svn_wc__db_status_base_deleted)
14130 {
14131 /* Let's return the kind of what is really deleted insead of what
14132 we have cached in the base-deleted record */
14133
14134 SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
14135
14136 if (!have_info)
14137 {
14138 /* No lower layer deleted? Database inconsistency! */
14139 *kind = svn_node_none;
14140 return svn_error_trace(svn_sqlite__reset(stmt_info));
14141 }
14142 }
14143
14144 if (!(show_deleted && show_hidden))
14145 {
14146 int op_depth = svn_sqlite__column_int(stmt_info, 0);
14147 svn_boolean_t report_none = FALSE;
14148
14149 if (op_depth > 0)
14150 SVN_ERR(convert_to_working_status(&status, status));
14151
14152 switch (status)
14153 {
14154 case svn_wc__db_status_not_present:
14155 if (! (show_hidden && show_deleted))
14156 report_none = TRUE;
14157 break;
14158 case svn_wc__db_status_excluded:
14159 case svn_wc__db_status_server_excluded:
14160 if (! show_hidden)
14161 report_none = TRUE;
14162 break;
14163 case svn_wc__db_status_deleted:
14164 if (! show_deleted)
14165 report_none = TRUE;
14166 break;
14167 default:
14168 break;
14169 }
14170
14171 if (report_none)
14172 {
14173 *kind = svn_node_none;
14174 return svn_error_trace(svn_sqlite__reset(stmt_info));
14175 }
14176 }
14177
14178 *kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
14179
14180 return svn_error_trace(svn_sqlite__reset(stmt_info));
14181 }
14182
14183 svn_error_t *
svn_wc__db_is_wcroot(svn_boolean_t * is_wcroot,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)14184 svn_wc__db_is_wcroot(svn_boolean_t *is_wcroot,
14185 svn_wc__db_t *db,
14186 const char *local_abspath,
14187 apr_pool_t *scratch_pool)
14188 {
14189 svn_wc__db_wcroot_t *wcroot;
14190 const char *local_relpath;
14191
14192 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14193
14194 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14195 local_abspath, scratch_pool, scratch_pool));
14196 VERIFY_USABLE_WCROOT(wcroot);
14197
14198 if (*local_relpath != '\0')
14199 {
14200 *is_wcroot = FALSE; /* Node is a file, or has a parent directory within
14201 the same wcroot */
14202 return SVN_NO_ERROR;
14203 }
14204
14205 *is_wcroot = TRUE;
14206
14207 return SVN_NO_ERROR;
14208 }
14209
14210 /* Find a node's kind and whether it is switched, putting the outputs in
14211 * *IS_SWITCHED and *KIND. Either of the outputs may be NULL if not wanted.
14212 */
14213 static svn_error_t *
db_is_switched(svn_boolean_t * is_switched,svn_node_kind_t * kind,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)14214 db_is_switched(svn_boolean_t *is_switched,
14215 svn_node_kind_t *kind,
14216 svn_wc__db_wcroot_t *wcroot,
14217 const char *local_relpath,
14218 apr_pool_t *scratch_pool)
14219 {
14220 svn_wc__db_status_t status;
14221 apr_int64_t repos_id;
14222 const char *repos_relpath;
14223 const char *name;
14224 const char *parent_local_relpath;
14225 apr_int64_t parent_repos_id;
14226 const char *parent_repos_relpath;
14227
14228 SVN_ERR_ASSERT(*local_relpath != '\0'); /* Handled in wrapper */
14229
14230 SVN_ERR(read_info(&status, kind, NULL, &repos_relpath, &repos_id, NULL,
14231 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
14232 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
14233 wcroot, local_relpath, scratch_pool, scratch_pool));
14234
14235 if (status == svn_wc__db_status_server_excluded
14236 || status == svn_wc__db_status_excluded
14237 || status == svn_wc__db_status_not_present)
14238 {
14239 return svn_error_createf(
14240 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14241 _("The node '%s' was not found."),
14242 path_for_error_message(wcroot, local_relpath,
14243 scratch_pool));
14244 }
14245 else if (! repos_relpath)
14246 {
14247 /* Node is shadowed; easy out */
14248 if (is_switched)
14249 *is_switched = FALSE;
14250
14251 return SVN_NO_ERROR;
14252 }
14253
14254 if (! is_switched)
14255 return SVN_NO_ERROR;
14256
14257 svn_relpath_split(&parent_local_relpath, &name, local_relpath, scratch_pool);
14258
14259 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
14260 &parent_repos_relpath,
14261 &parent_repos_id, NULL, NULL, NULL,
14262 NULL, NULL, NULL, NULL, NULL,
14263 NULL, NULL,
14264 wcroot, parent_local_relpath,
14265 scratch_pool, scratch_pool));
14266
14267 if (repos_id != parent_repos_id)
14268 *is_switched = TRUE;
14269 else
14270 {
14271 const char *expected_relpath;
14272
14273 expected_relpath = svn_relpath_join(parent_repos_relpath, name,
14274 scratch_pool);
14275
14276 *is_switched = (strcmp(expected_relpath, repos_relpath) != 0);
14277 }
14278
14279 return SVN_NO_ERROR;
14280 }
14281
14282 svn_error_t *
svn_wc__db_is_switched(svn_boolean_t * is_wcroot,svn_boolean_t * is_switched,svn_node_kind_t * kind,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)14283 svn_wc__db_is_switched(svn_boolean_t *is_wcroot,
14284 svn_boolean_t *is_switched,
14285 svn_node_kind_t *kind,
14286 svn_wc__db_t *db,
14287 const char *local_abspath,
14288 apr_pool_t *scratch_pool)
14289 {
14290 svn_wc__db_wcroot_t *wcroot;
14291 const char *local_relpath;
14292
14293 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14294
14295 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14296 local_abspath, scratch_pool, scratch_pool));
14297 VERIFY_USABLE_WCROOT(wcroot);
14298
14299 if (is_switched)
14300 *is_switched = FALSE;
14301
14302 if (*local_relpath == '\0')
14303 {
14304 /* Easy out */
14305 if (is_wcroot)
14306 *is_wcroot = TRUE;
14307
14308 if (kind)
14309 *kind = svn_node_dir;
14310 return SVN_NO_ERROR;
14311 }
14312
14313 if (is_wcroot)
14314 *is_wcroot = FALSE;
14315
14316 if (! is_switched && ! kind)
14317 return SVN_NO_ERROR;
14318
14319 SVN_WC__DB_WITH_TXN(
14320 db_is_switched(is_switched, kind, wcroot, local_relpath, scratch_pool),
14321 wcroot);
14322 return SVN_NO_ERROR;
14323 }
14324
14325
14326 svn_error_t *
svn_wc__db_temp_wcroot_tempdir(const char ** temp_dir_abspath,svn_wc__db_t * db,const char * wri_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)14327 svn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath,
14328 svn_wc__db_t *db,
14329 const char *wri_abspath,
14330 apr_pool_t *result_pool,
14331 apr_pool_t *scratch_pool)
14332 {
14333 svn_wc__db_wcroot_t *wcroot;
14334 const char *local_relpath;
14335
14336 SVN_ERR_ASSERT(temp_dir_abspath != NULL);
14337 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
14338
14339 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14340 wri_abspath, scratch_pool, scratch_pool));
14341 VERIFY_USABLE_WCROOT(wcroot);
14342
14343 *temp_dir_abspath = svn_dirent_join_many(result_pool,
14344 wcroot->abspath,
14345 svn_wc_get_adm_dir(scratch_pool),
14346 WCROOT_TEMPDIR_RELPATH,
14347 SVN_VA_NULL);
14348 return SVN_NO_ERROR;
14349 }
14350
14351
14352 /* Helper for wclock_obtain_cb() to steal an existing lock */
14353 static svn_error_t *
wclock_steal(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)14354 wclock_steal(svn_wc__db_wcroot_t *wcroot,
14355 const char *local_relpath,
14356 apr_pool_t *scratch_pool)
14357 {
14358 svn_sqlite__stmt_t *stmt;
14359
14360 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_WC_LOCK));
14361 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14362
14363 SVN_ERR(svn_sqlite__step_done(stmt));
14364
14365 return SVN_NO_ERROR;
14366 }
14367
14368
14369 /* The body of svn_wc__db_wclock_obtain().
14370 */
14371 static svn_error_t *
wclock_obtain_cb(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int levels_to_lock,svn_boolean_t steal_lock,svn_boolean_t enforce_empty_wq,apr_pool_t * scratch_pool)14372 wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot,
14373 const char *local_relpath,
14374 int levels_to_lock,
14375 svn_boolean_t steal_lock,
14376 svn_boolean_t enforce_empty_wq,
14377 apr_pool_t *scratch_pool)
14378 {
14379 svn_sqlite__stmt_t *stmt;
14380 svn_error_t *err;
14381 const char *lock_relpath;
14382 int max_depth;
14383 int lock_depth;
14384 svn_boolean_t got_row;
14385
14386 svn_wc__db_wclock_t lock;
14387
14388 /* Upgrade locks the root before the node exists. Apart from that
14389 the root node always exists so we will just skip the check.
14390
14391 ### Perhaps the lock for upgrade should be created when the db is
14392 created? 1.6 used to lock .svn on creation. */
14393 if (local_relpath[0])
14394 {
14395 svn_boolean_t exists;
14396
14397 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
14398 if (!exists)
14399 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14400 _("The node '%s' was not found."),
14401 path_for_error_message(wcroot,
14402 local_relpath,
14403 scratch_pool));
14404 }
14405
14406 if (enforce_empty_wq)
14407 SVN_ERR(svn_wc__db_verify_no_work(wcroot->sdb));
14408
14409 /* Check if there are nodes locked below the new lock root */
14410 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_FIND_WC_LOCK));
14411 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14412
14413 lock_depth = relpath_depth(local_relpath);
14414 max_depth = lock_depth + levels_to_lock;
14415
14416 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14417
14418 while (got_row)
14419 {
14420 svn_boolean_t own_lock;
14421
14422 lock_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
14423
14424 /* If we are not locking with depth infinity, check if this lock
14425 voids our lock request */
14426 if (levels_to_lock >= 0
14427 && relpath_depth(lock_relpath) > max_depth)
14428 {
14429 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14430 continue;
14431 }
14432
14433 /* Check if we are the lock owner, because we should be able to
14434 extend our lock. */
14435 err = svn_wc__db_wclock_owns_lock_internal(&own_lock, wcroot,
14436 lock_relpath,
14437 TRUE, scratch_pool);
14438
14439 if (err)
14440 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14441
14442 if (!own_lock && !steal_lock)
14443 {
14444 SVN_ERR(svn_sqlite__reset(stmt));
14445 err = svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
14446 _("'%s' is already locked."),
14447 path_for_error_message(wcroot,
14448 lock_relpath,
14449 scratch_pool));
14450 return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14451 _("Working copy '%s' locked."),
14452 path_for_error_message(wcroot,
14453 local_relpath,
14454 scratch_pool));
14455 }
14456 else if (!own_lock)
14457 {
14458 err = wclock_steal(wcroot, lock_relpath, scratch_pool);
14459
14460 if (err)
14461 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14462 }
14463
14464 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14465 }
14466
14467 SVN_ERR(svn_sqlite__reset(stmt));
14468
14469 if (steal_lock)
14470 SVN_ERR(wclock_steal(wcroot, local_relpath, scratch_pool));
14471
14472 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_WC_LOCK));
14473 lock_relpath = local_relpath;
14474
14475 while (TRUE)
14476 {
14477 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, lock_relpath));
14478
14479 SVN_ERR(svn_sqlite__step(&got_row, stmt));
14480
14481 if (got_row)
14482 {
14483 int levels = svn_sqlite__column_int(stmt, 0);
14484 if (levels >= 0)
14485 levels += relpath_depth(lock_relpath);
14486
14487 SVN_ERR(svn_sqlite__reset(stmt));
14488
14489 if (levels == -1 || levels >= lock_depth)
14490 {
14491
14492 err = svn_error_createf(
14493 SVN_ERR_WC_LOCKED, NULL,
14494 _("'%s' is already locked."),
14495 svn_dirent_local_style(
14496 svn_dirent_join(wcroot->abspath,
14497 lock_relpath,
14498 scratch_pool),
14499 scratch_pool));
14500 return svn_error_createf(
14501 SVN_ERR_WC_LOCKED, err,
14502 _("Working copy '%s' locked."),
14503 path_for_error_message(wcroot,
14504 local_relpath,
14505 scratch_pool));
14506 }
14507
14508 break; /* There can't be interesting locks on higher nodes */
14509 }
14510 else
14511 SVN_ERR(svn_sqlite__reset(stmt));
14512
14513 if (!*lock_relpath)
14514 break;
14515
14516 lock_relpath = svn_relpath_dirname(lock_relpath, scratch_pool);
14517 }
14518
14519 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_WC_LOCK));
14520 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14521 levels_to_lock));
14522 err = svn_sqlite__insert(NULL, stmt);
14523 if (err)
14524 return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14525 _("Failed to lock working copy '%s'."),
14526 path_for_error_message(wcroot,
14527 local_relpath,
14528 scratch_pool));
14529
14530 /* And finally store that we obtained the lock */
14531 lock.local_relpath = apr_pstrdup(wcroot->owned_locks->pool, local_relpath);
14532 lock.levels = levels_to_lock;
14533 APR_ARRAY_PUSH(wcroot->owned_locks, svn_wc__db_wclock_t) = lock;
14534
14535 return SVN_NO_ERROR;
14536 }
14537
14538
14539 svn_error_t *
svn_wc__db_wclock_obtain(svn_wc__db_t * db,const char * local_abspath,int levels_to_lock,svn_boolean_t steal_lock,apr_pool_t * scratch_pool)14540 svn_wc__db_wclock_obtain(svn_wc__db_t *db,
14541 const char *local_abspath,
14542 int levels_to_lock,
14543 svn_boolean_t steal_lock,
14544 apr_pool_t *scratch_pool)
14545 {
14546 svn_wc__db_wcroot_t *wcroot;
14547 const char *local_relpath;
14548
14549 SVN_ERR_ASSERT(levels_to_lock >= -1);
14550 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14551
14552 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14553 db, local_abspath,
14554 scratch_pool, scratch_pool));
14555 VERIFY_USABLE_WCROOT(wcroot);
14556
14557 if (!steal_lock)
14558 {
14559 int i;
14560 int depth = relpath_depth(local_relpath);
14561
14562 for (i = 0; i < wcroot->owned_locks->nelts; i++)
14563 {
14564 svn_wc__db_wclock_t* lock = &APR_ARRAY_IDX(wcroot->owned_locks,
14565 i, svn_wc__db_wclock_t);
14566
14567 if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14568 && (lock->levels == -1
14569 || (lock->levels + relpath_depth(lock->local_relpath))
14570 >= depth))
14571 {
14572 return svn_error_createf(
14573 SVN_ERR_WC_LOCKED, NULL,
14574 _("'%s' is already locked via '%s'."),
14575 svn_dirent_local_style(local_abspath, scratch_pool),
14576 path_for_error_message(wcroot, lock->local_relpath,
14577 scratch_pool));
14578 }
14579 }
14580 }
14581
14582 SVN_WC__DB_WITH_TXN(
14583 wclock_obtain_cb(wcroot, local_relpath, levels_to_lock, steal_lock,
14584 db->enforce_empty_wq, scratch_pool),
14585 wcroot);
14586 return SVN_NO_ERROR;
14587 }
14588
14589
14590 /* The body of svn_wc__db_wclock_find_root() and svn_wc__db_wclocked(). */
14591 static svn_error_t *
find_wclock(const char ** lock_relpath,svn_wc__db_wcroot_t * wcroot,const char * dir_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)14592 find_wclock(const char **lock_relpath,
14593 svn_wc__db_wcroot_t *wcroot,
14594 const char *dir_relpath,
14595 apr_pool_t *result_pool,
14596 apr_pool_t *scratch_pool)
14597 {
14598 svn_sqlite__stmt_t *stmt;
14599 svn_boolean_t have_row;
14600 int dir_depth = relpath_depth(dir_relpath);
14601 const char *first_relpath;
14602
14603 /* Check for locks on all directories that might be ancestors.
14604 As our new apis only use recursive locks the number of locks stored
14605 in the DB will be very low */
14606 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14607 STMT_SELECT_ANCESTOR_WCLOCKS));
14608
14609 /* Get the top level relpath to reduce the worst case number of results
14610 to the number of directories below this node plus two.
14611 (1: the node itself and 2: the wcroot). */
14612 first_relpath = strchr(dir_relpath, '/');
14613
14614 if (first_relpath != NULL)
14615 first_relpath = apr_pstrndup(scratch_pool, dir_relpath,
14616 first_relpath - dir_relpath);
14617 else
14618 first_relpath = dir_relpath;
14619
14620 SVN_ERR(svn_sqlite__bindf(stmt, "iss",
14621 wcroot->wc_id,
14622 dir_relpath,
14623 first_relpath));
14624
14625 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14626
14627 while (have_row)
14628 {
14629 const char *relpath = svn_sqlite__column_text(stmt, 0, NULL);
14630
14631 if (svn_relpath_skip_ancestor(relpath, dir_relpath))
14632 {
14633 int locked_levels = svn_sqlite__column_int(stmt, 1);
14634 int row_depth = relpath_depth(relpath);
14635
14636 if (locked_levels == -1
14637 || locked_levels + row_depth >= dir_depth)
14638 {
14639 *lock_relpath = apr_pstrdup(result_pool, relpath);
14640 SVN_ERR(svn_sqlite__reset(stmt));
14641 return SVN_NO_ERROR;
14642 }
14643 }
14644
14645 SVN_ERR(svn_sqlite__step(&have_row, stmt));
14646 }
14647
14648 *lock_relpath = NULL;
14649
14650 return svn_error_trace(svn_sqlite__reset(stmt));
14651 }
14652
14653 static svn_error_t *
is_wclocked(svn_boolean_t * locked,svn_wc__db_wcroot_t * wcroot,const char * dir_relpath,apr_pool_t * scratch_pool)14654 is_wclocked(svn_boolean_t *locked,
14655 svn_wc__db_wcroot_t *wcroot,
14656 const char *dir_relpath,
14657 apr_pool_t *scratch_pool)
14658 {
14659 const char *lock_relpath;
14660
14661 SVN_ERR(find_wclock(&lock_relpath, wcroot, dir_relpath,
14662 scratch_pool, scratch_pool));
14663 *locked = (lock_relpath != NULL);
14664 return SVN_NO_ERROR;
14665 }
14666
14667
14668 svn_error_t*
svn_wc__db_wclock_find_root(const char ** lock_abspath,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)14669 svn_wc__db_wclock_find_root(const char **lock_abspath,
14670 svn_wc__db_t *db,
14671 const char *local_abspath,
14672 apr_pool_t *result_pool,
14673 apr_pool_t *scratch_pool)
14674 {
14675 svn_wc__db_wcroot_t *wcroot;
14676 const char *local_relpath;
14677 const char *lock_relpath;
14678
14679 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14680 local_abspath, scratch_pool, scratch_pool));
14681 VERIFY_USABLE_WCROOT(wcroot);
14682
14683 SVN_WC__DB_WITH_TXN(
14684 find_wclock(&lock_relpath, wcroot, local_relpath,
14685 scratch_pool, scratch_pool),
14686 wcroot);
14687
14688 if (!lock_relpath)
14689 *lock_abspath = NULL;
14690 else
14691 SVN_ERR(svn_wc__db_from_relpath(lock_abspath, db, wcroot->abspath,
14692 lock_relpath, result_pool, scratch_pool));
14693 return SVN_NO_ERROR;
14694 }
14695
14696
14697 svn_error_t *
svn_wc__db_wclocked(svn_boolean_t * locked,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)14698 svn_wc__db_wclocked(svn_boolean_t *locked,
14699 svn_wc__db_t *db,
14700 const char *local_abspath,
14701 apr_pool_t *scratch_pool)
14702 {
14703 svn_wc__db_wcroot_t *wcroot;
14704 const char *local_relpath;
14705
14706 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14707 local_abspath, scratch_pool, scratch_pool));
14708 VERIFY_USABLE_WCROOT(wcroot);
14709
14710 SVN_WC__DB_WITH_TXN(
14711 is_wclocked(locked, wcroot, local_relpath, scratch_pool),
14712 wcroot);
14713
14714 return SVN_NO_ERROR;
14715 }
14716
14717
14718 svn_error_t *
svn_wc__db_wclock_release(svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)14719 svn_wc__db_wclock_release(svn_wc__db_t *db,
14720 const char *local_abspath,
14721 apr_pool_t *scratch_pool)
14722 {
14723 svn_sqlite__stmt_t *stmt;
14724 svn_wc__db_wcroot_t *wcroot;
14725 const char *local_relpath;
14726 int i;
14727 apr_array_header_t *owned_locks;
14728
14729 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14730 local_abspath, scratch_pool, scratch_pool));
14731
14732 VERIFY_USABLE_WCROOT(wcroot);
14733
14734 /* First check and remove the owns-lock information as failure in
14735 removing the db record implies that we have to steal the lock later. */
14736 owned_locks = wcroot->owned_locks;
14737 for (i = 0; i < owned_locks->nelts; i++)
14738 {
14739 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14740 svn_wc__db_wclock_t);
14741
14742 if (strcmp(lock->local_relpath, local_relpath) == 0)
14743 break;
14744 }
14745
14746 if (i >= owned_locks->nelts)
14747 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
14748 _("Working copy not locked at '%s'."),
14749 svn_dirent_local_style(local_abspath,
14750 scratch_pool));
14751
14752 if (i < owned_locks->nelts)
14753 {
14754 owned_locks->nelts--;
14755
14756 /* Move the last item in the array to the deleted place */
14757 if (owned_locks->nelts > 0)
14758 APR_ARRAY_IDX(owned_locks, i, svn_wc__db_wclock_t) =
14759 APR_ARRAY_IDX(owned_locks, owned_locks->nelts, svn_wc__db_wclock_t);
14760 }
14761
14762 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14763 STMT_DELETE_WC_LOCK));
14764
14765 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14766
14767 SVN_ERR(svn_sqlite__step_done(stmt));
14768
14769 return SVN_NO_ERROR;
14770 }
14771
14772
14773 /* Like svn_wc__db_wclock_owns_lock() but taking WCROOT+LOCAL_RELPATH instead
14774 of DB+LOCAL_ABSPATH. */
14775 svn_error_t *
svn_wc__db_wclock_owns_lock_internal(svn_boolean_t * own_lock,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_boolean_t exact,apr_pool_t * scratch_pool)14776 svn_wc__db_wclock_owns_lock_internal(svn_boolean_t *own_lock,
14777 svn_wc__db_wcroot_t *wcroot,
14778 const char *local_relpath,
14779 svn_boolean_t exact,
14780 apr_pool_t *scratch_pool)
14781 {
14782 apr_array_header_t *owned_locks;
14783 int lock_level;
14784 int i;
14785
14786 *own_lock = FALSE;
14787 owned_locks = wcroot->owned_locks;
14788 lock_level = relpath_depth(local_relpath);
14789
14790 if (exact)
14791 {
14792 for (i = 0; i < owned_locks->nelts; i++)
14793 {
14794 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14795 svn_wc__db_wclock_t);
14796
14797 if (strcmp(lock->local_relpath, local_relpath) == 0)
14798 {
14799 *own_lock = TRUE;
14800 return SVN_NO_ERROR;
14801 }
14802 }
14803 }
14804 else
14805 {
14806 for (i = 0; i < owned_locks->nelts; i++)
14807 {
14808 svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14809 svn_wc__db_wclock_t);
14810
14811 if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14812 && (lock->levels == -1
14813 || ((relpath_depth(lock->local_relpath) + lock->levels)
14814 >= lock_level)))
14815 {
14816 *own_lock = TRUE;
14817 return SVN_NO_ERROR;
14818 }
14819 }
14820 }
14821
14822 return SVN_NO_ERROR;
14823 }
14824
14825
14826 svn_error_t *
svn_wc__db_wclock_owns_lock(svn_boolean_t * own_lock,svn_wc__db_t * db,const char * local_abspath,svn_boolean_t exact,apr_pool_t * scratch_pool)14827 svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
14828 svn_wc__db_t *db,
14829 const char *local_abspath,
14830 svn_boolean_t exact,
14831 apr_pool_t *scratch_pool)
14832 {
14833 svn_wc__db_wcroot_t *wcroot;
14834 const char *local_relpath;
14835
14836 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14837 local_abspath, scratch_pool, scratch_pool));
14838
14839 if (!wcroot)
14840 return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
14841 _("The node '%s' was not found."),
14842 svn_dirent_local_style(local_abspath,
14843 scratch_pool));
14844
14845 VERIFY_USABLE_WCROOT(wcroot);
14846
14847 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(own_lock, wcroot, local_relpath,
14848 exact, scratch_pool));
14849
14850 return SVN_NO_ERROR;
14851 }
14852
14853 /* The body of svn_wc__db_temp_op_end_directory_update().
14854 */
14855 static svn_error_t *
end_directory_update(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)14856 end_directory_update(svn_wc__db_wcroot_t *wcroot,
14857 const char *local_relpath,
14858 apr_pool_t *scratch_pool)
14859 {
14860 svn_sqlite__stmt_t *stmt;
14861 svn_wc__db_status_t base_status;
14862
14863 SVN_ERR(svn_wc__db_base_get_info_internal(&base_status, NULL, NULL, NULL,
14864 NULL, NULL, NULL, NULL, NULL,
14865 NULL, NULL, NULL, NULL, NULL, NULL,
14866 wcroot, local_relpath,
14867 scratch_pool, scratch_pool));
14868
14869 if (base_status == svn_wc__db_status_normal)
14870 return SVN_NO_ERROR;
14871
14872 SVN_ERR_ASSERT(base_status == svn_wc__db_status_incomplete);
14873
14874 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14875 STMT_UPDATE_NODE_BASE_PRESENCE));
14876 SVN_ERR(svn_sqlite__bindf(stmt, "ist", wcroot->wc_id, local_relpath,
14877 presence_map, svn_wc__db_status_normal));
14878 SVN_ERR(svn_sqlite__step_done(stmt));
14879
14880 return SVN_NO_ERROR;
14881 }
14882
14883 svn_error_t *
svn_wc__db_temp_op_end_directory_update(svn_wc__db_t * db,const char * local_dir_abspath,apr_pool_t * scratch_pool)14884 svn_wc__db_temp_op_end_directory_update(svn_wc__db_t *db,
14885 const char *local_dir_abspath,
14886 apr_pool_t *scratch_pool)
14887 {
14888 svn_wc__db_wcroot_t *wcroot;
14889 const char *local_relpath;
14890
14891 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
14892
14893 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14894 local_dir_abspath, scratch_pool, scratch_pool));
14895 VERIFY_USABLE_WCROOT(wcroot);
14896
14897 SVN_WC__DB_WITH_TXN(
14898 end_directory_update(wcroot, local_relpath, scratch_pool),
14899 wcroot);
14900
14901 SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_empty,
14902 scratch_pool));
14903
14904 return SVN_NO_ERROR;
14905 }
14906
14907
14908 /* The body of svn_wc__db_temp_op_start_directory_update().
14909 */
14910 static svn_error_t *
start_directory_update_txn(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,const char * new_repos_relpath,svn_revnum_t new_rev,apr_pool_t * scratch_pool)14911 start_directory_update_txn(svn_wc__db_wcroot_t *wcroot,
14912 const char *local_relpath,
14913 const char *new_repos_relpath,
14914 svn_revnum_t new_rev,
14915 apr_pool_t *scratch_pool)
14916 {
14917 svn_sqlite__stmt_t *stmt;
14918
14919 /* Note: In the majority of calls, the repos_relpath is unchanged. */
14920 /* ### TODO: Maybe check if we can make repos_relpath NULL. */
14921 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14922 STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH));
14923
14924 SVN_ERR(svn_sqlite__bindf(stmt, "istrs",
14925 wcroot->wc_id,
14926 local_relpath,
14927 presence_map, svn_wc__db_status_incomplete,
14928 new_rev,
14929 new_repos_relpath));
14930 SVN_ERR(svn_sqlite__step_done(stmt));
14931
14932 return SVN_NO_ERROR;
14933
14934 }
14935
14936 svn_error_t *
svn_wc__db_temp_op_start_directory_update(svn_wc__db_t * db,const char * local_abspath,const char * new_repos_relpath,svn_revnum_t new_rev,apr_pool_t * scratch_pool)14937 svn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db,
14938 const char *local_abspath,
14939 const char *new_repos_relpath,
14940 svn_revnum_t new_rev,
14941 apr_pool_t *scratch_pool)
14942 {
14943 svn_wc__db_wcroot_t *wcroot;
14944 const char *local_relpath;
14945
14946 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14947 SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_rev));
14948 SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
14949
14950 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14951 local_abspath, scratch_pool, scratch_pool));
14952 VERIFY_USABLE_WCROOT(wcroot);
14953
14954 SVN_WC__DB_WITH_TXN(
14955 start_directory_update_txn(wcroot, local_relpath,
14956 new_repos_relpath, new_rev, scratch_pool),
14957 wcroot);
14958
14959 SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
14960
14961 return SVN_NO_ERROR;
14962 }
14963
14964 /* Helper for svn_wc__db_op_make_copy_internal */
14965 static svn_error_t *
db_move_moved_to(svn_wc__db_wcroot_t * wcroot,const char * src1_relpath,int src1_op_depth,const char * src2_relpath,int src2_op_depth,const char * dst_relpath,apr_pool_t * scratch_pool)14966 db_move_moved_to(svn_wc__db_wcroot_t *wcroot,
14967 const char *src1_relpath,
14968 int src1_op_depth,
14969 const char *src2_relpath,
14970 int src2_op_depth,
14971 const char *dst_relpath,
14972 apr_pool_t *scratch_pool)
14973 {
14974 svn_sqlite__stmt_t *stmt;
14975 int affected_rows;
14976
14977 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14978 STMT_UPDATE_MOVED_TO_RELPATH));
14979 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
14980 src1_relpath, src1_op_depth));
14981 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
14982
14983 if (affected_rows == 1)
14984 {
14985 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14986 STMT_UPDATE_MOVED_TO_RELPATH));
14987 SVN_ERR(svn_sqlite__bindf(stmt, "isds", wcroot->wc_id,
14988 src2_relpath, src2_op_depth,
14989 dst_relpath));
14990 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
14991 }
14992 if (affected_rows != 1)
14993 return svn_error_create(SVN_ERR_WC_PATH_NOT_FOUND, NULL, NULL);
14994
14995 return SVN_NO_ERROR;
14996 }
14997
14998 static svn_error_t *
db_move_moved_to_down_recursive(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,int new_shadow_layer,apr_pool_t * scratch_pool)14999 db_move_moved_to_down_recursive(svn_wc__db_wcroot_t *wcroot,
15000 const char *local_relpath,
15001 int new_shadow_layer,
15002 apr_pool_t *scratch_pool)
15003 {
15004 svn_sqlite__stmt_t *stmt;
15005 svn_boolean_t have_row;
15006 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15007
15008 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15009 STMT_SELECT_MOVED_DESCENDANTS_SRC));
15010 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15011 new_shadow_layer));
15012 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15013
15014 while (have_row)
15015 {
15016 int del_op_depth;
15017 const char *src_relpath;
15018 const char *dst_relpath;
15019 svn_error_t *err;
15020
15021 svn_pool_clear(iterpool);
15022
15023 del_op_depth = svn_sqlite__column_int(stmt, 0);
15024 src_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
15025 dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool);
15026
15027 err = svn_error_trace(
15028 db_move_moved_to(
15029 wcroot,
15030 src_relpath, del_op_depth,
15031 src_relpath, new_shadow_layer,
15032 dst_relpath, iterpool));
15033
15034 if (err)
15035 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
15036
15037 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15038 }
15039
15040 SVN_ERR(svn_sqlite__reset(stmt));
15041
15042 return SVN_NO_ERROR;
15043 }
15044
15045
15046 /* The body of svn_wc__db_temp_op_make_copy(). This is
15047 used by the update editor when deleting a base node tree would be a
15048 tree-conflict because there are changes to subtrees. This function
15049 inserts a copy of the base node tree below any existing working
15050 subtrees. Given a tree:
15051
15052 0 1 2 3
15053 / normal -
15054 A normal -
15055 A/B normal - normal
15056 A/B/C normal - base-del normal
15057 A/F normal - normal
15058 A/F/G normal - normal
15059 A/F/H normal - base-deleted normal
15060 A/F/E normal - not-present
15061 A/X normal -
15062 A/X/Y incomplete -
15063
15064 This function adds layers to A and some of its descendants in an attempt
15065 to make the working copy look like as if it were a copy of the BASE nodes.
15066
15067 0 1 2 3
15068 / normal -
15069 A normal norm
15070 A/B normal norm norm
15071 A/B/C normal norm base-del normal
15072 A/F normal norm norm
15073 A/F/G normal norm norm
15074 A/F/H normal norm not-pres
15075 A/F/E normal norm base-del
15076 A/X normal norm
15077 A/X/Y incomplete incomplete
15078 */
15079 static svn_error_t *
make_copy_txn(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_int64_t last_repos_id,const char * last_repos_relpath,svn_revnum_t last_revision,int last_op_depth,svn_boolean_t shadowed,int root_shadow_depth,apr_pool_t * scratch_pool)15080 make_copy_txn(svn_wc__db_wcroot_t *wcroot,
15081 const char *local_relpath,
15082 apr_int64_t last_repos_id,
15083 const char *last_repos_relpath,
15084 svn_revnum_t last_revision,
15085 int last_op_depth,
15086 svn_boolean_t shadowed,
15087 int root_shadow_depth,
15088 apr_pool_t *scratch_pool)
15089 {
15090 svn_sqlite__stmt_t *stmt;
15091 svn_boolean_t have_row = FALSE;
15092 svn_revnum_t revision;
15093 apr_int64_t repos_id;
15094 const char *repos_relpath;
15095 svn_node_kind_t kind;
15096 int op_depth = relpath_depth(local_relpath);
15097
15098 if (last_op_depth != op_depth)
15099 {
15100 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15101 STMT_SELECT_DEPTH_NODE));
15102 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15103 op_depth));
15104 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15105 SVN_ERR(svn_sqlite__reset(stmt));
15106 if (have_row)
15107 shadowed = TRUE;
15108 }
15109
15110 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &kind, &revision,
15111 &repos_relpath, &repos_id, NULL,
15112 NULL, NULL, NULL, NULL, NULL, NULL,
15113 NULL, NULL, NULL,
15114 wcroot, local_relpath,
15115 scratch_pool, scratch_pool));
15116
15117 if (last_repos_relpath
15118 && repos_id == last_repos_id
15119 && revision == last_revision)
15120 {
15121 const char *name = svn_relpath_skip_ancestor(last_repos_relpath,
15122 repos_relpath);
15123
15124 if (name && strcmp(name, svn_relpath_basename(local_relpath, NULL)) == 0)
15125 op_depth = last_op_depth;
15126 }
15127
15128 /* Can we add a new copy node at the wanted op-depth? */
15129 if (!have_row || op_depth == last_op_depth)
15130 {
15131 int i;
15132
15133 SVN_ERR(svn_sqlite__get_statement(
15134 &stmt, wcroot->sdb,
15135 STMT_INSERT_WORKING_NODE_FROM_BASE_COPY));
15136 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15137 op_depth));
15138 SVN_ERR(svn_sqlite__step_done(stmt));
15139
15140 if (shadowed)
15141 SVN_ERR(db_extend_parent_delete(wcroot, local_relpath, kind,
15142 op_depth, scratch_pool));
15143
15144 if (kind == svn_node_dir)
15145 {
15146 const apr_array_header_t *children;
15147 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15148
15149 SVN_ERR(gather_children(&children, wcroot, local_relpath,
15150 STMT_SELECT_OP_DEPTH_CHILDREN, 0,
15151 scratch_pool, iterpool));
15152
15153 for (i = 0; i < children->nelts; i++)
15154 {
15155 const char *name = APR_ARRAY_IDX(children, i, const char *);
15156 const char *copy_relpath;
15157
15158 svn_pool_clear(iterpool);
15159
15160 copy_relpath = svn_relpath_join(local_relpath, name, iterpool);
15161
15162 SVN_ERR(make_copy_txn(wcroot, copy_relpath,
15163 repos_id, repos_relpath, revision,
15164 op_depth, shadowed, root_shadow_depth,
15165 scratch_pool));
15166 }
15167 svn_pool_destroy(iterpool);
15168 }
15169 }
15170 else
15171 {
15172 /* Auch... we can't make a copy of whatever comes deeper, as this
15173 op-depth is already filled by something else. Let's hope
15174 the user doesn't mind.
15175
15176 Luckily we know that the moves are already moved to the shadowing
15177 layer, so we can just remove dangling base-deletes if there are
15178 any.
15179 */
15180 /* BASE_DELETED may be at op_depth, so let's use last_op_depth! */
15181 SVN_ERR(db_move_moved_to_down_recursive(wcroot, local_relpath,
15182 root_shadow_depth,
15183 scratch_pool));
15184
15185 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15186 STMT_DELETE_WORKING_BASE_DELETE));
15187 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15188 last_op_depth));
15189 SVN_ERR(svn_sqlite__step_done(stmt));
15190 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15191 STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE));
15192 SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15193 last_op_depth));
15194 SVN_ERR(svn_sqlite__step_done(stmt));
15195 }
15196
15197 /* Insert a not-present node to mark that we don't know what exists here.
15198
15199 We do this last (after recursing), to allow the move fix-up code to
15200 see the original moves. */
15201 if (last_op_depth > 0 && last_op_depth != op_depth)
15202 {
15203 insert_working_baton_t iwb;
15204
15205 blank_iwb(&iwb);
15206 iwb.presence = svn_wc__db_status_not_present;
15207 iwb.op_depth = last_op_depth;
15208
15209 iwb.original_repos_id = repos_id;
15210 iwb.original_repos_relpath = repos_relpath;
15211 iwb.original_revnum = revision;
15212 iwb.kind = kind;
15213
15214 SVN_ERR(insert_working_node(&iwb, wcroot, local_relpath, scratch_pool));
15215 }
15216
15217 return SVN_NO_ERROR;
15218 }
15219
15220
15221 svn_error_t *
svn_wc__db_op_make_copy_internal(svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_boolean_t move_move_info,const svn_skel_t * conflicts,const svn_skel_t * work_items,apr_pool_t * scratch_pool)15222 svn_wc__db_op_make_copy_internal(svn_wc__db_wcroot_t *wcroot,
15223 const char *local_relpath,
15224 svn_boolean_t move_move_info,
15225 const svn_skel_t *conflicts,
15226 const svn_skel_t *work_items,
15227 apr_pool_t *scratch_pool)
15228 {
15229 svn_sqlite__stmt_t *stmt;
15230 svn_boolean_t have_row;
15231 int op_depth = -1;
15232
15233 /* The update editor is supposed to call this function when there is
15234 no working node for LOCAL_ABSPATH. */
15235 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15236 STMT_SELECT_WORKING_NODE));
15237 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15238 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15239 if (have_row)
15240 op_depth = svn_sqlite__column_int(stmt, 0);
15241 SVN_ERR(svn_sqlite__reset(stmt));
15242
15243 if (have_row)
15244 {
15245 if (op_depth == relpath_depth(local_relpath))
15246 return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
15247 _("Modification of '%s' already exists"),
15248 path_for_error_message(wcroot,
15249 local_relpath,
15250 scratch_pool));
15251
15252 /* We have a working layer, but not one at the op-depth of local-relpath,
15253 so we can create a copy by just copying the lower layer */
15254
15255 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15256 STMT_COPY_OP_DEPTH_RECURSIVE));
15257 SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath,
15258 op_depth, relpath_depth(local_relpath)));
15259 SVN_ERR(svn_sqlite__step_done(stmt));
15260 }
15261 else
15262 {
15263 int affected_rows;
15264
15265 op_depth = relpath_depth(local_relpath);
15266 /* We don't allow copies to contain server-excluded nodes;
15267 the update editor is going to have to bail out. */
15268 SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath,
15269 scratch_pool));
15270
15271 /* Insert a shadowing layer */
15272 SVN_ERR(svn_sqlite__get_statement(
15273 &stmt, wcroot->sdb,
15274 STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
15275
15276 /* As we are keeping whatever is below, move the*/
15277
15278 SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
15279 wcroot->wc_id, local_relpath,
15280 0, op_depth));
15281 SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
15282 SVN_ERR_ASSERT(affected_rows > 0);
15283
15284 if (!move_move_info)
15285 SVN_ERR(db_move_moved_to_down_recursive(wcroot, local_relpath,
15286 op_depth, scratch_pool));
15287
15288
15289 SVN_ERR(make_copy_txn(wcroot, local_relpath,
15290 INVALID_REPOS_ID, NULL, SVN_INVALID_REVNUM,
15291 op_depth, FALSE, op_depth,
15292 scratch_pool));
15293 }
15294
15295 if (conflicts)
15296 SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
15297 conflicts, scratch_pool));
15298
15299 SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
15300
15301 return SVN_NO_ERROR;
15302 }
15303
15304
15305 svn_error_t *
svn_wc__db_op_make_copy(svn_wc__db_t * db,const char * local_abspath,const svn_skel_t * conflicts,const svn_skel_t * work_items,apr_pool_t * scratch_pool)15306 svn_wc__db_op_make_copy(svn_wc__db_t *db,
15307 const char *local_abspath,
15308 const svn_skel_t *conflicts,
15309 const svn_skel_t *work_items,
15310 apr_pool_t *scratch_pool)
15311 {
15312 svn_wc__db_wcroot_t *wcroot;
15313 const char *local_relpath;
15314
15315 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15316
15317 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15318 local_abspath, scratch_pool, scratch_pool));
15319 VERIFY_USABLE_WCROOT(wcroot);
15320
15321 SVN_WC__DB_WITH_TXN(
15322 svn_wc__db_op_make_copy_internal(wcroot, local_relpath, FALSE,
15323 conflicts, work_items,
15324 scratch_pool),
15325 wcroot);
15326
15327 SVN_ERR(flush_entries(wcroot, local_abspath,
15328 svn_depth_infinity, scratch_pool));
15329
15330 return SVN_NO_ERROR;
15331 }
15332
15333 svn_error_t *
svn_wc__db_info_below_working(svn_boolean_t * have_base,svn_boolean_t * have_work,svn_wc__db_status_t * status,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)15334 svn_wc__db_info_below_working(svn_boolean_t *have_base,
15335 svn_boolean_t *have_work,
15336 svn_wc__db_status_t *status,
15337 svn_wc__db_t *db,
15338 const char *local_abspath,
15339 apr_pool_t *scratch_pool)
15340 {
15341 svn_wc__db_wcroot_t *wcroot;
15342 const char *local_relpath;
15343
15344 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15345
15346 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15347 local_abspath, scratch_pool, scratch_pool));
15348 VERIFY_USABLE_WCROOT(wcroot);
15349 SVN_ERR(info_below_working(have_base, have_work, status,
15350 wcroot, local_relpath, -1, scratch_pool));
15351
15352 return SVN_NO_ERROR;
15353 }
15354
15355 svn_error_t *
svn_wc__db_get_not_present_descendants(const apr_array_header_t ** descendants,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)15356 svn_wc__db_get_not_present_descendants(const apr_array_header_t **descendants,
15357 svn_wc__db_t *db,
15358 const char *local_abspath,
15359 apr_pool_t *result_pool,
15360 apr_pool_t *scratch_pool)
15361 {
15362 svn_wc__db_wcroot_t *wcroot;
15363 const char *local_relpath;
15364 svn_sqlite__stmt_t *stmt;
15365 svn_boolean_t have_row;
15366
15367 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15368
15369 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15370 local_abspath, scratch_pool, scratch_pool));
15371 VERIFY_USABLE_WCROOT(wcroot);
15372
15373 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15374 STMT_SELECT_NOT_PRESENT_DESCENDANTS));
15375
15376 SVN_ERR(svn_sqlite__bindf(stmt, "isd",
15377 wcroot->wc_id,
15378 local_relpath,
15379 relpath_depth(local_relpath)));
15380
15381 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15382
15383 if (have_row)
15384 {
15385 apr_array_header_t *paths;
15386
15387 paths = apr_array_make(result_pool, 4, sizeof(const char*));
15388 while (have_row)
15389 {
15390 const char *found_relpath = svn_sqlite__column_text(stmt, 0, NULL);
15391
15392 APR_ARRAY_PUSH(paths, const char *)
15393 = apr_pstrdup(result_pool, svn_relpath_skip_ancestor(
15394 local_relpath, found_relpath));
15395
15396 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15397 }
15398
15399 *descendants = paths;
15400 }
15401 else
15402 *descendants = apr_array_make(result_pool, 0, sizeof(const char*));
15403
15404 return svn_error_trace(svn_sqlite__reset(stmt));
15405 }
15406
15407
15408 /* Like svn_wc__db_min_max_revisions(),
15409 * but accepts a WCROOT/LOCAL_RELPATH pair. */
15410 static svn_error_t *
get_min_max_revisions(svn_revnum_t * min_revision,svn_revnum_t * max_revision,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_boolean_t committed,apr_pool_t * scratch_pool)15411 get_min_max_revisions(svn_revnum_t *min_revision,
15412 svn_revnum_t *max_revision,
15413 svn_wc__db_wcroot_t *wcroot,
15414 const char *local_relpath,
15415 svn_boolean_t committed,
15416 apr_pool_t *scratch_pool)
15417 {
15418 svn_sqlite__stmt_t *stmt;
15419 svn_revnum_t min_rev, max_rev;
15420
15421 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15422 STMT_SELECT_MIN_MAX_REVISIONS));
15423 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15424 SVN_ERR(svn_sqlite__step_row(stmt));
15425
15426 if (committed)
15427 {
15428 min_rev = svn_sqlite__column_revnum(stmt, 2);
15429 max_rev = svn_sqlite__column_revnum(stmt, 3);
15430 }
15431 else
15432 {
15433 min_rev = svn_sqlite__column_revnum(stmt, 0);
15434 max_rev = svn_sqlite__column_revnum(stmt, 1);
15435 }
15436
15437 /* The statement returns exactly one row. */
15438 SVN_ERR(svn_sqlite__reset(stmt));
15439
15440 if (min_revision)
15441 *min_revision = min_rev;
15442 if (max_revision)
15443 *max_revision = max_rev;
15444
15445 return SVN_NO_ERROR;
15446 }
15447
15448
15449 svn_error_t *
svn_wc__db_min_max_revisions(svn_revnum_t * min_revision,svn_revnum_t * max_revision,svn_wc__db_t * db,const char * local_abspath,svn_boolean_t committed,apr_pool_t * scratch_pool)15450 svn_wc__db_min_max_revisions(svn_revnum_t *min_revision,
15451 svn_revnum_t *max_revision,
15452 svn_wc__db_t *db,
15453 const char *local_abspath,
15454 svn_boolean_t committed,
15455 apr_pool_t *scratch_pool)
15456 {
15457 svn_wc__db_wcroot_t *wcroot;
15458 const char *local_relpath;
15459
15460 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15461
15462 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15463 db, local_abspath,
15464 scratch_pool, scratch_pool));
15465 VERIFY_USABLE_WCROOT(wcroot);
15466
15467 return svn_error_trace(get_min_max_revisions(min_revision, max_revision,
15468 wcroot, local_relpath,
15469 committed, scratch_pool));
15470 }
15471
15472
15473 /* Set *IS_SPARSE_CHECKOUT TRUE if LOCAL_RELPATH or any of the nodes
15474 * within LOCAL_RELPATH is sparse, FALSE otherwise. */
15475 static svn_error_t *
is_sparse_checkout_internal(svn_boolean_t * is_sparse_checkout,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)15476 is_sparse_checkout_internal(svn_boolean_t *is_sparse_checkout,
15477 svn_wc__db_wcroot_t *wcroot,
15478 const char *local_relpath,
15479 apr_pool_t *scratch_pool)
15480 {
15481 svn_sqlite__stmt_t *stmt;
15482 svn_boolean_t have_row;
15483
15484 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15485 STMT_HAS_SPARSE_NODES));
15486 SVN_ERR(svn_sqlite__bindf(stmt, "is",
15487 wcroot->wc_id,
15488 local_relpath));
15489 /* If this query returns a row, the working copy is sparse. */
15490 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15491 *is_sparse_checkout = have_row;
15492 SVN_ERR(svn_sqlite__reset(stmt));
15493
15494 return SVN_NO_ERROR;
15495 }
15496
15497
15498 /* Like svn_wc__db_has_switched_subtrees(),
15499 * but accepts a WCROOT/LOCAL_RELPATH pair. */
15500 static svn_error_t *
has_switched_subtrees(svn_boolean_t * is_switched,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,const char * trail_url,apr_pool_t * scratch_pool)15501 has_switched_subtrees(svn_boolean_t *is_switched,
15502 svn_wc__db_wcroot_t *wcroot,
15503 const char *local_relpath,
15504 const char *trail_url,
15505 apr_pool_t *scratch_pool)
15506 {
15507 svn_sqlite__stmt_t *stmt;
15508 svn_boolean_t have_row;
15509 apr_int64_t repos_id;
15510 const char *repos_relpath;
15511
15512 /* Optional argument handling for caller */
15513 if (!is_switched)
15514 return SVN_NO_ERROR;
15515
15516 *is_switched = FALSE;
15517
15518 SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
15519 &repos_relpath, &repos_id,
15520 NULL, NULL, NULL, NULL, NULL,
15521 NULL, NULL, NULL, NULL, NULL,
15522 wcroot, local_relpath,
15523 scratch_pool, scratch_pool));
15524
15525 /* First do the cheap check where we only need info on the origin itself */
15526 if (trail_url != NULL)
15527 {
15528 const char *repos_root_url;
15529 const char *url;
15530 apr_size_t len1, len2;
15531
15532 /* If the trailing part of the URL of the working copy directory
15533 does not match the given trailing URL then the whole working
15534 copy is switched. */
15535
15536 SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot,
15537 repos_id, scratch_pool));
15538 url = svn_path_url_add_component2(repos_root_url, repos_relpath,
15539 scratch_pool);
15540
15541 len1 = strlen(trail_url);
15542 len2 = strlen(url);
15543 if ((len1 > len2) || strcmp(url + len2 - len1, trail_url))
15544 {
15545 *is_switched = TRUE;
15546 return SVN_NO_ERROR;
15547 }
15548 }
15549
15550 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_HAS_SWITCHED));
15551 SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath, repos_relpath));
15552 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15553 if (have_row)
15554 *is_switched = TRUE;
15555 SVN_ERR(svn_sqlite__reset(stmt));
15556
15557 return SVN_NO_ERROR;
15558 }
15559
15560
15561 svn_error_t *
svn_wc__db_has_switched_subtrees(svn_boolean_t * is_switched,svn_wc__db_t * db,const char * local_abspath,const char * trail_url,apr_pool_t * scratch_pool)15562 svn_wc__db_has_switched_subtrees(svn_boolean_t *is_switched,
15563 svn_wc__db_t *db,
15564 const char *local_abspath,
15565 const char *trail_url,
15566 apr_pool_t *scratch_pool)
15567 {
15568 svn_wc__db_wcroot_t *wcroot;
15569 const char *local_relpath;
15570
15571 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15572
15573 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15574 db, local_abspath,
15575 scratch_pool, scratch_pool));
15576 VERIFY_USABLE_WCROOT(wcroot);
15577
15578 return svn_error_trace(has_switched_subtrees(is_switched, wcroot,
15579 local_relpath, trail_url,
15580 scratch_pool));
15581 }
15582
15583 svn_error_t *
svn_wc__db_get_excluded_subtrees(apr_hash_t ** excluded_subtrees,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)15584 svn_wc__db_get_excluded_subtrees(apr_hash_t **excluded_subtrees,
15585 svn_wc__db_t *db,
15586 const char *local_abspath,
15587 apr_pool_t *result_pool,
15588 apr_pool_t *scratch_pool)
15589 {
15590 svn_wc__db_wcroot_t *wcroot;
15591 const char *local_relpath;
15592 svn_sqlite__stmt_t *stmt;
15593 svn_boolean_t have_row;
15594
15595 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15596 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15597 db, local_abspath,
15598 scratch_pool, scratch_pool));
15599 VERIFY_USABLE_WCROOT(wcroot);
15600
15601 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15602 STMT_SELECT_ALL_EXCLUDED_DESCENDANTS));
15603 SVN_ERR(svn_sqlite__bindf(stmt, "is",
15604 wcroot->wc_id,
15605 local_relpath));
15606 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15607
15608 if (have_row)
15609 *excluded_subtrees = apr_hash_make(result_pool);
15610 else
15611 *excluded_subtrees = NULL;
15612
15613 while (have_row)
15614 {
15615 const char *abs_path =
15616 svn_dirent_join(wcroot->abspath,
15617 svn_sqlite__column_text(stmt, 0, NULL),
15618 result_pool);
15619 svn_hash_sets(*excluded_subtrees, abs_path, abs_path);
15620 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15621 }
15622
15623 SVN_ERR(svn_sqlite__reset(stmt));
15624 return SVN_NO_ERROR;
15625 }
15626
15627 /* Like svn_wc__db_has_db_mods(),
15628 * but accepts a WCROOT/LOCAL_RELPATH pair.
15629 * ### This needs a DB as well as a WCROOT/RELPATH pair... */
15630 static svn_error_t *
has_db_mods(svn_boolean_t * is_modified,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,apr_pool_t * scratch_pool)15631 has_db_mods(svn_boolean_t *is_modified,
15632 svn_wc__db_wcroot_t *wcroot,
15633 const char *local_relpath,
15634 apr_pool_t *scratch_pool)
15635 {
15636 svn_sqlite__stmt_t *stmt;
15637
15638 /* Check for additions or deletions. */
15639 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15640 STMT_SUBTREE_HAS_TREE_MODIFICATIONS));
15641 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15642 /* If this query returns a row, the working copy is modified. */
15643 SVN_ERR(svn_sqlite__step(is_modified, stmt));
15644 SVN_ERR(svn_sqlite__reset(stmt));
15645
15646 if (! *is_modified)
15647 {
15648 /* Check for property modifications. */
15649 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15650 STMT_SUBTREE_HAS_PROP_MODIFICATIONS));
15651 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15652 /* If this query returns a row, the working copy is modified. */
15653 SVN_ERR(svn_sqlite__step(is_modified, stmt));
15654 SVN_ERR(svn_sqlite__reset(stmt));
15655 }
15656
15657 return SVN_NO_ERROR;
15658 }
15659
15660
15661 svn_error_t *
svn_wc__db_has_db_mods(svn_boolean_t * is_modified,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)15662 svn_wc__db_has_db_mods(svn_boolean_t *is_modified,
15663 svn_wc__db_t *db,
15664 const char *local_abspath,
15665 apr_pool_t *scratch_pool)
15666 {
15667 svn_wc__db_wcroot_t *wcroot;
15668 const char *local_relpath;
15669
15670 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15671
15672 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15673 db, local_abspath,
15674 scratch_pool, scratch_pool));
15675 VERIFY_USABLE_WCROOT(wcroot);
15676
15677 return svn_error_trace(has_db_mods(is_modified, wcroot, local_relpath,
15678 scratch_pool));
15679 }
15680
15681
15682 /* The body of svn_wc__db_revision_status().
15683 */
15684 static svn_error_t *
revision_status_txn(svn_revnum_t * min_revision,svn_revnum_t * max_revision,svn_boolean_t * is_sparse_checkout,svn_boolean_t * is_modified,svn_boolean_t * is_switched,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_wc__db_t * db,const char * trail_url,svn_boolean_t committed,apr_pool_t * scratch_pool)15685 revision_status_txn(svn_revnum_t *min_revision,
15686 svn_revnum_t *max_revision,
15687 svn_boolean_t *is_sparse_checkout,
15688 svn_boolean_t *is_modified,
15689 svn_boolean_t *is_switched,
15690 svn_wc__db_wcroot_t *wcroot,
15691 const char *local_relpath,
15692 svn_wc__db_t *db,
15693 const char *trail_url,
15694 svn_boolean_t committed,
15695 apr_pool_t *scratch_pool)
15696 {
15697 svn_error_t *err;
15698 svn_boolean_t exists;
15699
15700 SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
15701
15702 if (!exists)
15703 {
15704 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
15705 _("The node '%s' was not found."),
15706 path_for_error_message(wcroot, local_relpath,
15707 scratch_pool));
15708 }
15709
15710 /* Determine mixed-revisionness. */
15711 SVN_ERR(get_min_max_revisions(min_revision, max_revision, wcroot,
15712 local_relpath, committed, scratch_pool));
15713
15714 /* Determine sparseness. */
15715 SVN_ERR(is_sparse_checkout_internal(is_sparse_checkout, wcroot,
15716 local_relpath, scratch_pool));
15717
15718 /* Check for switched nodes. */
15719 {
15720 err = has_switched_subtrees(is_switched, wcroot, local_relpath,
15721 trail_url, scratch_pool);
15722
15723 if (err)
15724 {
15725 if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
15726 return svn_error_trace(err);
15727
15728 svn_error_clear(err); /* No Base node, but no fatal error */
15729 *is_switched = FALSE;
15730 }
15731 }
15732
15733 /* Check for db mods. */
15734 SVN_ERR(has_db_mods(is_modified, wcroot, local_relpath, scratch_pool));
15735
15736 return SVN_NO_ERROR;
15737 }
15738
15739
15740 svn_error_t *
svn_wc__db_revision_status(svn_revnum_t * min_revision,svn_revnum_t * max_revision,svn_boolean_t * is_sparse_checkout,svn_boolean_t * is_modified,svn_boolean_t * is_switched,svn_wc__db_t * db,const char * local_abspath,const char * trail_url,svn_boolean_t committed,apr_pool_t * scratch_pool)15741 svn_wc__db_revision_status(svn_revnum_t *min_revision,
15742 svn_revnum_t *max_revision,
15743 svn_boolean_t *is_sparse_checkout,
15744 svn_boolean_t *is_modified,
15745 svn_boolean_t *is_switched,
15746 svn_wc__db_t *db,
15747 const char *local_abspath,
15748 const char *trail_url,
15749 svn_boolean_t committed,
15750 apr_pool_t *scratch_pool)
15751 {
15752 svn_wc__db_wcroot_t *wcroot;
15753 const char *local_relpath;
15754
15755 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15756
15757 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15758 db, local_abspath,
15759 scratch_pool, scratch_pool));
15760 VERIFY_USABLE_WCROOT(wcroot);
15761
15762 SVN_WC__DB_WITH_TXN(
15763 revision_status_txn(min_revision, max_revision,
15764 is_sparse_checkout, is_modified, is_switched,
15765 wcroot, local_relpath, db,
15766 trail_url, committed,
15767 scratch_pool),
15768 wcroot);
15769 return SVN_NO_ERROR;
15770 }
15771
15772
15773 svn_error_t *
svn_wc__db_base_get_lock_tokens_recursive(apr_hash_t ** lock_tokens,svn_wc__db_t * db,const char * local_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)15774 svn_wc__db_base_get_lock_tokens_recursive(apr_hash_t **lock_tokens,
15775 svn_wc__db_t *db,
15776 const char *local_abspath,
15777 apr_pool_t *result_pool,
15778 apr_pool_t *scratch_pool)
15779 {
15780 svn_wc__db_wcroot_t *wcroot;
15781 const char *local_relpath;
15782 svn_sqlite__stmt_t *stmt;
15783 svn_boolean_t have_row;
15784 apr_int64_t last_repos_id = INVALID_REPOS_ID;
15785 const char *last_repos_root_url = NULL;
15786
15787 SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15788
15789 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15790 db, local_abspath,
15791 scratch_pool, scratch_pool));
15792 VERIFY_USABLE_WCROOT(wcroot);
15793
15794 *lock_tokens = apr_hash_make(result_pool);
15795
15796 /* Fetch all the lock tokens in and under LOCAL_RELPATH. */
15797 SVN_ERR(svn_sqlite__get_statement(
15798 &stmt, wcroot->sdb,
15799 STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE));
15800
15801 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15802 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15803 while (have_row)
15804 {
15805 apr_int64_t child_repos_id = svn_sqlite__column_int64(stmt, 0);
15806 const char *child_relpath = svn_sqlite__column_text(stmt, 1, NULL);
15807 const char *lock_token = svn_sqlite__column_text(stmt, 2, result_pool);
15808
15809 if (child_repos_id != last_repos_id)
15810 {
15811 svn_error_t *err = svn_wc__db_fetch_repos_info(&last_repos_root_url,
15812 NULL, wcroot,
15813 child_repos_id,
15814 scratch_pool);
15815
15816 if (err)
15817 {
15818 return svn_error_trace(
15819 svn_error_compose_create(err,
15820 svn_sqlite__reset(stmt)));
15821 }
15822
15823 last_repos_id = child_repos_id;
15824 }
15825
15826 SVN_ERR_ASSERT(last_repos_root_url != NULL);
15827 svn_hash_sets(*lock_tokens,
15828 svn_path_url_add_component2(last_repos_root_url,
15829 child_relpath, result_pool),
15830 lock_token);
15831
15832 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15833 }
15834 return svn_sqlite__reset(stmt);
15835 }
15836
15837
15838 /* If EXPRESSION is false, cause the caller to return an SVN_ERR_WC_CORRUPT
15839 * error, showing EXPRESSION and the caller's LOCAL_RELPATH in the message. */
15840 #define VERIFY(expression) \
15841 do { \
15842 if (! (expression)) \
15843 return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, \
15844 _("database inconsistency at local_relpath='%s' verifying " \
15845 "expression '%s'"), local_relpath, #expression); \
15846 } while (0)
15847
15848
15849 /* Verify consistency of the metadata concerning WCROOT. This is intended
15850 * for use only during testing and debugging, so is not intended to be
15851 * blazingly fast.
15852 *
15853 * This code is a complement to any verification that we can do in SQLite
15854 * triggers. See, for example, 'wc-checks.sql'.
15855 *
15856 * Some more verification steps we might want to add are:
15857 *
15858 * * on every ACTUAL row (except root): a NODES row exists at its parent path
15859 * * the op-depth root must always exist and every intermediate too
15860 */
15861 static svn_error_t *
verify_wcroot(svn_wc__db_wcroot_t * wcroot,apr_pool_t * scratch_pool)15862 verify_wcroot(svn_wc__db_wcroot_t *wcroot,
15863 apr_pool_t *scratch_pool)
15864 {
15865 svn_sqlite__stmt_t *stmt;
15866 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15867
15868 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15869 STMT_SELECT_ALL_NODES));
15870 SVN_ERR(svn_sqlite__bindf(stmt, "i", wcroot->wc_id));
15871 while (TRUE)
15872 {
15873 svn_boolean_t have_row;
15874 const char *local_relpath, *parent_relpath;
15875 int op_depth;
15876
15877 svn_pool_clear(iterpool);
15878
15879 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15880 if (!have_row)
15881 break;
15882
15883 op_depth = svn_sqlite__column_int(stmt, 0);
15884 local_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
15885 parent_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
15886
15887 /* Verify parent_relpath is the parent path of local_relpath */
15888 VERIFY((parent_relpath == NULL)
15889 ? (local_relpath[0] == '\0')
15890 : (strcmp(svn_relpath_dirname(local_relpath, iterpool),
15891 parent_relpath) == 0));
15892
15893 /* Verify op_depth <= the tree depth of local_relpath */
15894 VERIFY(op_depth <= relpath_depth(local_relpath));
15895
15896 /* Verify parent_relpath refers to a row that exists */
15897 /* TODO: Verify there is a suitable parent row - e.g. has op_depth <=
15898 * the child's and a suitable presence */
15899 if (parent_relpath && svn_sqlite__column_is_null(stmt, 3))
15900 {
15901 svn_sqlite__stmt_t *stmt2;
15902 svn_boolean_t have_a_parent_row;
15903
15904 SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
15905 STMT_SELECT_NODE_INFO));
15906 SVN_ERR(svn_sqlite__bindf(stmt2, "is", wcroot->wc_id,
15907 parent_relpath));
15908 SVN_ERR(svn_sqlite__step(&have_a_parent_row, stmt2));
15909 VERIFY(have_a_parent_row);
15910 SVN_ERR(svn_sqlite__reset(stmt2));
15911 }
15912 }
15913 svn_pool_destroy(iterpool);
15914
15915 return svn_error_trace(svn_sqlite__reset(stmt));
15916 }
15917
15918 svn_error_t *
svn_wc__db_verify(svn_wc__db_t * db,const char * wri_abspath,apr_pool_t * scratch_pool)15919 svn_wc__db_verify(svn_wc__db_t *db,
15920 const char *wri_abspath,
15921 apr_pool_t *scratch_pool)
15922 {
15923 svn_wc__db_wcroot_t *wcroot;
15924 const char *local_relpath;
15925
15926 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15927 db, wri_abspath,
15928 scratch_pool, scratch_pool));
15929 VERIFY_USABLE_WCROOT(wcroot);
15930
15931 SVN_ERR(verify_wcroot(wcroot, scratch_pool));
15932 return SVN_NO_ERROR;
15933 }
15934
15935
15936 svn_error_t *
svn_wc__db_verify_db_full_internal(svn_wc__db_wcroot_t * wcroot,svn_wc__db_verify_cb_t callback,void * baton,apr_pool_t * scratch_pool)15937 svn_wc__db_verify_db_full_internal(svn_wc__db_wcroot_t *wcroot,
15938 svn_wc__db_verify_cb_t callback,
15939 void *baton,
15940 apr_pool_t *scratch_pool)
15941 {
15942 svn_sqlite__stmt_t *stmt;
15943 svn_boolean_t have_row;
15944 svn_error_t *err = NULL;
15945 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15946
15947 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_STATIC_VERIFY));
15948 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15949
15950 while (have_row)
15951 {
15952 const char *local_relpath;
15953 int op_depth = svn_sqlite__column_int(stmt, 1);
15954 int id = svn_sqlite__column_int(stmt, 2);
15955 const char *msg;
15956
15957 svn_pool_clear(iterpool);
15958
15959 local_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
15960 msg = svn_sqlite__column_text(stmt, 3, scratch_pool);
15961
15962 err = callback(baton, wcroot->abspath, local_relpath, op_depth,
15963 id, msg, iterpool);
15964
15965 if (err)
15966 break;
15967
15968 SVN_ERR(svn_sqlite__step(&have_row, stmt));
15969 }
15970
15971 svn_pool_destroy(iterpool);
15972
15973 return svn_error_trace(
15974 svn_error_compose_create(err, svn_sqlite__reset(stmt)));
15975 }
15976
15977 svn_error_t *
svn_wc__db_verify_db_full(svn_wc__db_t * db,const char * wri_abspath,svn_wc__db_verify_cb_t callback,void * baton,apr_pool_t * scratch_pool)15978 svn_wc__db_verify_db_full(svn_wc__db_t *db,
15979 const char *wri_abspath,
15980 svn_wc__db_verify_cb_t callback,
15981 void *baton,
15982 apr_pool_t *scratch_pool)
15983 {
15984 svn_wc__db_wcroot_t *wcroot;
15985 const char *local_relpath;
15986
15987 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
15988
15989 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15990 wri_abspath, scratch_pool, scratch_pool));
15991 VERIFY_USABLE_WCROOT(wcroot);
15992
15993 return svn_error_trace(
15994 svn_wc__db_verify_db_full_internal(wcroot, callback, baton,
15995 scratch_pool));
15996 }
15997
15998 svn_error_t *
svn_wc__db_bump_format(int * result_format,svn_boolean_t * bumped_format,svn_wc__db_t * db,const char * wcroot_abspath,apr_pool_t * scratch_pool)15999 svn_wc__db_bump_format(int *result_format,
16000 svn_boolean_t *bumped_format,
16001 svn_wc__db_t *db,
16002 const char *wcroot_abspath,
16003 apr_pool_t *scratch_pool)
16004 {
16005 svn_sqlite__db_t *sdb;
16006 svn_error_t *err;
16007 int format;
16008
16009 if (bumped_format)
16010 *bumped_format = FALSE;
16011
16012 /* Do not scan upwards for a working copy root here to prevent accidental
16013 * upgrades of any working copies the WCROOT might be nested in.
16014 * Just try to open a DB at the specified path instead. */
16015 err = svn_wc__db_util_open_db(&sdb, wcroot_abspath, SDB_FILE,
16016 svn_sqlite__mode_readwrite,
16017 TRUE, /* exclusive */
16018 0, /* default timeout */
16019 NULL, /* my statements */
16020 scratch_pool, scratch_pool);
16021 if (err)
16022 {
16023 svn_error_t *err2;
16024 apr_hash_t *entries;
16025
16026 /* Could not open an sdb. Check for an entries file instead. */
16027 err2 = svn_wc__read_entries_old(&entries, wcroot_abspath,
16028 scratch_pool, scratch_pool);
16029 if (err2 || apr_hash_count(entries) == 0)
16030 return svn_error_createf(SVN_ERR_WC_INVALID_OP_ON_CWD,
16031 svn_error_compose_create(err, err2),
16032 _("Can't upgrade '%s' as it is not a working copy root"),
16033 svn_dirent_local_style(wcroot_abspath, scratch_pool));
16034
16035 /* An entries file was found. This is a pre-wc-ng working copy
16036 * so suggest an upgrade. */
16037 return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, err,
16038 _("Working copy '%s' is too old and must be upgraded to "
16039 "at least format %d, as created by Subversion %s"),
16040 svn_dirent_local_style(wcroot_abspath, scratch_pool),
16041 SVN_WC__WC_NG_VERSION,
16042 svn_wc__version_string_from_format(SVN_WC__WC_NG_VERSION));
16043 }
16044
16045 SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool));
16046 err = svn_wc__upgrade_sdb(result_format, wcroot_abspath,
16047 sdb, format, scratch_pool);
16048
16049 if (err == SVN_NO_ERROR && bumped_format)
16050 *bumped_format = (*result_format > format);
16051
16052 /* Make sure we return a different error than expected for upgrades from
16053 entries */
16054 if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
16055 err = svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, err,
16056 _("Working copy upgrade failed"));
16057
16058 err = svn_error_compose_create(err, svn_sqlite__close(sdb));
16059
16060 return svn_error_trace(err);
16061 }
16062
16063 svn_error_t *
svn_wc__db_vacuum(svn_wc__db_t * db,const char * local_abspath,apr_pool_t * scratch_pool)16064 svn_wc__db_vacuum(svn_wc__db_t *db,
16065 const char *local_abspath,
16066 apr_pool_t *scratch_pool)
16067 {
16068 svn_wc__db_wcroot_t *wcroot;
16069 const char *local_relpath;
16070
16071 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
16072 db, local_abspath,
16073 scratch_pool, scratch_pool));
16074 SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_VACUUM));
16075
16076 return SVN_NO_ERROR;
16077 }
16078
16079 /* Item queued with svn_wc__db_commit_queue_add */
16080 typedef struct commit_queue_item_t
16081 {
16082 const char *local_relpath;
16083 svn_boolean_t recurse; /* Use legacy recursion */
16084 svn_boolean_t committed; /* Process the node as committed */
16085 svn_boolean_t remove_lock; /* Remove existing lock on node */
16086 svn_boolean_t remove_changelist; /* Remove changelist on node */
16087
16088 /* The pristine text checksum. NULL if the old value should be kept
16089 and for directories */
16090 const svn_checksum_t *new_sha1_checksum;
16091
16092 apr_hash_t *new_dav_cache; /* New DAV cache for the node */
16093 } commit_queue_item_t;
16094
16095 /* The queue definition for vn_wc__db_create_commit_queue,
16096 svn_wc__db_commit_queue_add and finally svn_wc__db_process_commit_queue */
16097 struct svn_wc__db_commit_queue_t
16098 {
16099 svn_wc__db_wcroot_t *wcroot; /* Wcroot for ITEMS */
16100 apr_array_header_t *items; /* List of commit_queue_item_t* */
16101 svn_boolean_t have_recurse; /* Is one or more item[x]->recurse TRUE? */
16102 };
16103
16104 /* Create a new svn_wc__db_commit_queue_t instance in RESULT_POOL for the
16105 working copy specified with WRI_ABSPATH */
16106 svn_error_t *
svn_wc__db_create_commit_queue(svn_wc__db_commit_queue_t ** queue,svn_wc__db_t * db,const char * wri_abspath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)16107 svn_wc__db_create_commit_queue(svn_wc__db_commit_queue_t **queue,
16108 svn_wc__db_t *db,
16109 const char *wri_abspath,
16110 apr_pool_t *result_pool,
16111 apr_pool_t *scratch_pool)
16112 {
16113 svn_wc__db_wcroot_t *wcroot;
16114 const char *local_relpath;
16115 svn_wc__db_commit_queue_t *q;
16116
16117 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
16118
16119 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
16120 wri_abspath, result_pool, scratch_pool));
16121 VERIFY_USABLE_WCROOT(wcroot);
16122
16123 q = apr_pcalloc(result_pool, sizeof(*q));
16124
16125 SVN_ERR_ASSERT(wcroot->sdb);
16126
16127 q->wcroot = wcroot;
16128 q->items = apr_array_make(result_pool, 64,
16129 sizeof(commit_queue_item_t*));
16130 q->have_recurse = FALSE;
16131
16132 *queue = q;
16133 return SVN_NO_ERROR;
16134 }
16135
16136 svn_error_t *
svn_wc__db_commit_queue_add(svn_wc__db_commit_queue_t * queue,const char * local_abspath,svn_boolean_t recurse,svn_boolean_t is_commited,svn_boolean_t remove_lock,svn_boolean_t remove_changelist,const svn_checksum_t * new_sha1_checksum,apr_hash_t * new_dav_cache,apr_pool_t * result_pool,apr_pool_t * scratch_pool)16137 svn_wc__db_commit_queue_add(svn_wc__db_commit_queue_t *queue,
16138 const char *local_abspath,
16139 svn_boolean_t recurse,
16140 svn_boolean_t is_commited,
16141 svn_boolean_t remove_lock,
16142 svn_boolean_t remove_changelist,
16143 const svn_checksum_t *new_sha1_checksum,
16144 apr_hash_t *new_dav_cache,
16145 apr_pool_t *result_pool,
16146 apr_pool_t *scratch_pool)
16147 {
16148 commit_queue_item_t *cqi;
16149 const char *local_relpath;
16150
16151 local_relpath = svn_dirent_skip_ancestor(queue->wcroot->abspath,
16152 local_abspath);
16153
16154 if (! local_relpath)
16155 return svn_error_createf(
16156 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
16157 _("The path '%s' is not in the working copy '%s'"),
16158 svn_dirent_local_style(local_abspath, scratch_pool),
16159 svn_dirent_local_style(queue->wcroot->abspath, scratch_pool));
16160
16161 cqi = apr_pcalloc(result_pool, sizeof(*cqi));
16162 cqi->local_relpath = local_relpath;
16163 cqi->recurse = recurse;
16164 cqi->committed = is_commited;
16165 cqi->remove_lock = remove_lock;
16166 cqi->remove_changelist = remove_changelist;
16167 cqi->new_sha1_checksum = new_sha1_checksum;
16168 cqi->new_dav_cache = new_dav_cache;
16169
16170 queue->have_recurse |= recurse;
16171
16172 APR_ARRAY_PUSH(queue->items, commit_queue_item_t *) = cqi;
16173 return SVN_NO_ERROR;
16174 }
16175
16176 /*** Finishing updates and commits. ***/
16177
16178 /* Post process an item that is committed in the repository. Collapse layers into
16179 * BASE. Queue work items that will finish a commit of the file or directory
16180 * LOCAL_ABSPATH in DB:
16181 */
16182 static svn_error_t *
process_committed_leaf(svn_wc__db_t * db,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_boolean_t via_recurse,svn_wc__db_status_t status,svn_node_kind_t kind,svn_boolean_t prop_mods,const svn_checksum_t * old_checksum,svn_revnum_t new_revnum,apr_time_t new_changed_date,const char * new_changed_author,apr_hash_t * new_dav_cache,svn_boolean_t remove_lock,svn_boolean_t remove_changelist,const svn_checksum_t * checksum,apr_pool_t * scratch_pool)16183 process_committed_leaf(svn_wc__db_t *db,
16184 svn_wc__db_wcroot_t *wcroot,
16185 const char *local_relpath,
16186 svn_boolean_t via_recurse,
16187 svn_wc__db_status_t status,
16188 svn_node_kind_t kind,
16189 svn_boolean_t prop_mods,
16190 const svn_checksum_t *old_checksum,
16191 svn_revnum_t new_revnum,
16192 apr_time_t new_changed_date,
16193 const char *new_changed_author,
16194 apr_hash_t *new_dav_cache,
16195 svn_boolean_t remove_lock,
16196 svn_boolean_t remove_changelist,
16197 const svn_checksum_t *checksum,
16198 apr_pool_t *scratch_pool)
16199 {
16200 svn_revnum_t new_changed_rev = new_revnum;
16201 svn_skel_t *work_item = NULL;
16202
16203 {
16204 const char *lock_relpath;
16205 svn_boolean_t locked;
16206
16207 if (kind == svn_node_dir)
16208 lock_relpath = local_relpath;
16209 else
16210 lock_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
16211
16212 SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot,
16213 lock_relpath, FALSE,
16214 scratch_pool));
16215
16216 if (!locked)
16217 return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
16218 _("No write-lock in '%s'"),
16219 path_for_error_message(wcroot, local_relpath,
16220 scratch_pool));
16221
16222 SVN_ERR(flush_entries(wcroot, lock_relpath, svn_depth_empty,
16223 scratch_pool));
16224 }
16225
16226 if (status == svn_wc__db_status_not_present)
16227 {
16228 /* We are committing the leaf of a copy operation.
16229 We leave the not-present marker to allow pulling in excluded
16230 children of a copy.
16231
16232 The next update will remove the not-present marker. */
16233
16234 return SVN_NO_ERROR;
16235 }
16236
16237 SVN_ERR_ASSERT(status == svn_wc__db_status_normal
16238 || status == svn_wc__db_status_incomplete
16239 || status == svn_wc__db_status_added
16240 || status == svn_wc__db_status_deleted);
16241
16242 if (kind != svn_node_dir
16243 && status != svn_wc__db_status_deleted)
16244 {
16245 /* If we sent a delta (meaning: post-copy modification),
16246 then this file will appear in the queue and so we should have
16247 its checksum already. */
16248 if (checksum == NULL)
16249 {
16250 /* It was copied and not modified. We must have a text
16251 base for it. And the node should have a checksum. */
16252 SVN_ERR_ASSERT(old_checksum != NULL);
16253
16254 checksum = old_checksum;
16255
16256 /* Is the node completely unmodified and are we recursing? */
16257 if (via_recurse && !prop_mods)
16258 {
16259 /* If a copied node itself is not modified, but the op_root of
16260 the copy is committed we have to make sure that changed_rev,
16261 changed_date and changed_author don't change or the working
16262 copy used for committing will show different last modified
16263 information then a clean checkout of exactly the same
16264 revisions. (Issue #3676) */
16265
16266 SVN_ERR(svn_wc__db_read_info_internal(
16267 NULL, NULL, NULL, NULL, NULL,
16268 &new_changed_rev,
16269 &new_changed_date,
16270 &new_changed_author, NULL, NULL,
16271 NULL, NULL, NULL, NULL, NULL,
16272 NULL, NULL, NULL, NULL,
16273 NULL, NULL, NULL, NULL,
16274 NULL, NULL,
16275 wcroot, local_relpath,
16276 scratch_pool, scratch_pool));
16277 }
16278 }
16279
16280 SVN_ERR(svn_wc__wq_build_file_commit(&work_item,
16281 db, svn_dirent_join(wcroot->abspath,
16282 local_relpath,
16283 scratch_pool),
16284 prop_mods,
16285 scratch_pool, scratch_pool));
16286 }
16287
16288 /* The new text base will be found in the pristine store by its checksum. */
16289 SVN_ERR(commit_node(wcroot, local_relpath,
16290 new_revnum, new_changed_rev,
16291 new_changed_date, new_changed_author,
16292 checksum,
16293 new_dav_cache,
16294 !remove_changelist,
16295 !remove_lock,
16296 work_item,
16297 scratch_pool));
16298
16299 return SVN_NO_ERROR;
16300 }
16301
16302 /** Internal helper for svn_wc_process_committed_queue2().
16303 * Bump a commit item, collapsing local changes with the new repository
16304 * information to a new BASE node.
16305 *
16306 * @a new_date is the (server-side) date of the new revision, or 0.
16307 *
16308 * @a rev_author is the (server-side) author of the new
16309 * revision; it may be @c NULL.
16310 *
16311 * @a new_dav_cache is a hash of all the new dav properties for LOCAL_RELPATH.
16312 *
16313 * If @a remove_lock is set, release any user locks on @a
16314 * local_abspath; otherwise keep them during processing.
16315 *
16316 * If @a remove_changelist is set, clear any changeset assignments
16317 * from @a local_abspath; otherwise, keep such assignments.
16318 *
16319 * If @a new_sha1_checksum is non-NULL, use it to identify the node's pristine
16320 * text.
16321 *
16322 * Set TOP_OF_RECURSE to TRUE to show that this the top of a possibly
16323 * recursive commit operation. (Part of the legacy recurse handling)
16324 */
16325 static svn_error_t *
process_committed_internal(svn_wc__db_t * db,svn_wc__db_wcroot_t * wcroot,const char * local_relpath,svn_boolean_t recurse,svn_boolean_t top_of_recurse,svn_revnum_t new_revnum,apr_time_t new_date,const char * rev_author,apr_hash_t * new_dav_cache,svn_boolean_t remove_lock,svn_boolean_t remove_changelist,const svn_checksum_t * new_sha1_checksum,apr_hash_t * items_by_relpath,apr_pool_t * scratch_pool)16326 process_committed_internal(svn_wc__db_t *db,
16327 svn_wc__db_wcroot_t *wcroot,
16328 const char *local_relpath,
16329 svn_boolean_t recurse,
16330 svn_boolean_t top_of_recurse,
16331 svn_revnum_t new_revnum,
16332 apr_time_t new_date,
16333 const char *rev_author,
16334 apr_hash_t *new_dav_cache,
16335 svn_boolean_t remove_lock,
16336 svn_boolean_t remove_changelist,
16337 const svn_checksum_t *new_sha1_checksum,
16338 apr_hash_t *items_by_relpath,
16339 apr_pool_t *scratch_pool)
16340 {
16341 svn_wc__db_status_t status;
16342 svn_node_kind_t kind;
16343 const svn_checksum_t *old_checksum;
16344 svn_boolean_t prop_mods;
16345
16346 SVN_ERR(svn_wc__db_read_info_internal(&status, &kind, NULL, NULL, NULL, NULL, NULL,
16347 NULL, NULL, &old_checksum, NULL, NULL,
16348 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
16349 NULL, &prop_mods, NULL, NULL, NULL,
16350 wcroot, local_relpath,
16351 scratch_pool, scratch_pool));
16352
16353 /* NOTE: be wary of making crazy semantic changes in this function, since
16354 svn_wc_process_committed4() calls this. */
16355
16356 SVN_ERR(process_committed_leaf(db, wcroot, local_relpath, !top_of_recurse,
16357 status, kind, prop_mods, old_checksum,
16358 new_revnum, new_date, rev_author,
16359 new_dav_cache,
16360 remove_lock, remove_changelist,
16361 new_sha1_checksum,
16362 scratch_pool));
16363
16364 /* Only check for recursion on nodes that have children */
16365 if (kind != svn_node_dir
16366 || status == svn_wc__db_status_not_present
16367 || status == svn_wc__db_status_excluded
16368 || status == svn_wc__db_status_server_excluded
16369 /* Node deleted -> then no longer a directory */
16370 || status == svn_wc__db_status_deleted)
16371 {
16372 return SVN_NO_ERROR;
16373 }
16374
16375 if (recurse)
16376 {
16377 const apr_array_header_t *children;
16378 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
16379 int i;
16380
16381 /* Read PATH's entries; this is the absolute path. */
16382 SVN_ERR(gather_children(&children, wcroot, local_relpath,
16383 STMT_SELECT_NODE_CHILDREN, -1,
16384 scratch_pool, iterpool));
16385
16386 /* Recursively loop over all children. */
16387 for (i = 0; i < children->nelts; i++)
16388 {
16389 const char *name = APR_ARRAY_IDX(children, i, const char *);
16390 const char *this_relpath;
16391 const commit_queue_item_t *cqi;
16392
16393 svn_pool_clear(iterpool);
16394
16395 this_relpath = svn_dirent_join(local_relpath, name, iterpool);
16396
16397 new_sha1_checksum = NULL;
16398 cqi = svn_hash_gets(items_by_relpath, this_relpath);
16399
16400 if (cqi != NULL)
16401 new_sha1_checksum = cqi->new_sha1_checksum;
16402
16403 /* Recurse. Pass NULL for NEW_DAV_CACHE, because the
16404 ones present in the current call are only applicable to
16405 this one committed item. */
16406 SVN_ERR(process_committed_internal(
16407 db, wcroot, this_relpath,
16408 TRUE /* recurse */,
16409 FALSE /* top_of_recurse */,
16410 new_revnum, new_date,
16411 rev_author,
16412 NULL /* new_dav_cache */,
16413 FALSE /* remove_lock */,
16414 remove_changelist,
16415 new_sha1_checksum,
16416 items_by_relpath,
16417 iterpool));
16418 }
16419
16420 svn_pool_destroy(iterpool);
16421 }
16422
16423 return SVN_NO_ERROR;
16424 }
16425
16426 /* Return TRUE if any item of QUEUE is a parent of ITEM and will be
16427 processed recursively, return FALSE otherwise.
16428
16429 The algorithmic complexity of this search implementation is O(queue
16430 length), but it's quite quick.
16431 */
16432 static svn_boolean_t
have_recursive_parent(const apr_array_header_t * all_items,const commit_queue_item_t * item,apr_pool_t * scratch_pool)16433 have_recursive_parent(const apr_array_header_t *all_items,
16434 const commit_queue_item_t *item,
16435 apr_pool_t *scratch_pool)
16436 {
16437 const char *local_relpath = item->local_relpath;
16438 int i;
16439
16440 for (i = 0; i < all_items->nelts; i++)
16441 {
16442 const commit_queue_item_t *qi
16443 = APR_ARRAY_IDX(all_items, i, const commit_queue_item_t *);
16444
16445 if (qi == item)
16446 continue;
16447
16448 if (qi->recurse && svn_relpath_skip_ancestor(qi->local_relpath,
16449 local_relpath))
16450 {
16451 return TRUE;
16452 }
16453 }
16454
16455 return FALSE;
16456 }
16457
16458 /* Compare function for svn_sort__array */
16459 static int
compare_queue_items(const void * v1,const void * v2)16460 compare_queue_items(const void *v1,
16461 const void *v2)
16462 {
16463 const commit_queue_item_t *cqi1
16464 = *(const commit_queue_item_t **)v1;
16465 const commit_queue_item_t *cqi2
16466 = *(const commit_queue_item_t **)v2;
16467
16468 return svn_path_compare_paths(cqi1->local_relpath, cqi2->local_relpath);
16469 }
16470
16471 /* Internal, locked version of svn_wc__db_process_commit_queue */
16472 static svn_error_t *
db_process_commit_queue(svn_wc__db_t * db,svn_wc__db_commit_queue_t * queue,svn_revnum_t new_revnum,apr_time_t new_date,const char * new_author,apr_pool_t * scratch_pool)16473 db_process_commit_queue(svn_wc__db_t *db,
16474 svn_wc__db_commit_queue_t *queue,
16475 svn_revnum_t new_revnum,
16476 apr_time_t new_date,
16477 const char *new_author,
16478 apr_pool_t *scratch_pool)
16479 {
16480 apr_hash_t *items_by_relpath = NULL;
16481 int j;
16482 apr_pool_t *iterpool = svn_pool_create(scratch_pool);
16483
16484 svn_sort__array(queue->items, compare_queue_items);
16485
16486 if (queue->have_recurse)
16487 {
16488 items_by_relpath = apr_hash_make(scratch_pool);
16489
16490 for (j = 0; j < queue->items->nelts; j++)
16491 {
16492 commit_queue_item_t *cqi
16493 = APR_ARRAY_IDX(queue->items, j, commit_queue_item_t *);
16494
16495 svn_hash_sets(items_by_relpath, cqi->local_relpath, cqi);
16496 }
16497 }
16498
16499 for (j = 0; j < queue->items->nelts; j++)
16500 {
16501 commit_queue_item_t *cqi
16502 = APR_ARRAY_IDX(queue->items, j, commit_queue_item_t *);
16503
16504 svn_pool_clear(iterpool);
16505
16506 /* Skip this item if it is a child of a recursive item, because it has
16507 been (or will be) accounted for when that recursive item was (or
16508 will be) processed. */
16509 if (queue->have_recurse && have_recursive_parent(queue->items, cqi,
16510 iterpool))
16511 continue;
16512
16513 if (!cqi->committed)
16514 {
16515 if (cqi->remove_lock)
16516 {
16517 svn_skel_t *work_item;
16518
16519 SVN_ERR(svn_wc__wq_build_sync_file_flags(
16520 &work_item,
16521 db,
16522 svn_dirent_join(
16523 queue->wcroot->abspath,
16524 cqi->local_relpath,
16525 iterpool),
16526 iterpool, iterpool));
16527
16528 SVN_ERR(lock_remove_txn(queue->wcroot, cqi->local_relpath,
16529 work_item, iterpool));
16530 }
16531 if (cqi->remove_changelist)
16532 SVN_ERR(svn_wc__db_op_set_changelist(db,
16533 svn_dirent_join(
16534 queue->wcroot->abspath,
16535 cqi->local_relpath,
16536 iterpool),
16537 NULL, NULL,
16538 svn_depth_empty,
16539 NULL, NULL, /* notify */
16540 NULL, NULL, /* cancel */
16541 iterpool));
16542 }
16543 else
16544 {
16545 SVN_ERR(process_committed_internal(
16546 db, queue->wcroot, cqi->local_relpath,
16547 cqi->recurse,
16548 TRUE /* top_of_recurse */,
16549 new_revnum, new_date, new_author,
16550 cqi->new_dav_cache,
16551 cqi->remove_lock,
16552 cqi->remove_changelist,
16553 cqi->new_sha1_checksum,
16554 items_by_relpath,
16555 iterpool));
16556 }
16557 }
16558
16559 svn_pool_destroy(iterpool);
16560
16561 return SVN_NO_ERROR;
16562 }
16563
16564 svn_error_t *
svn_wc__db_process_commit_queue(svn_wc__db_t * db,svn_wc__db_commit_queue_t * queue,svn_revnum_t new_revnum,apr_time_t new_date,const char * new_author,apr_pool_t * scratch_pool)16565 svn_wc__db_process_commit_queue(svn_wc__db_t *db,
16566 svn_wc__db_commit_queue_t *queue,
16567 svn_revnum_t new_revnum,
16568 apr_time_t new_date,
16569 const char *new_author,
16570 apr_pool_t *scratch_pool)
16571 {
16572 SVN_WC__DB_WITH_TXN(db_process_commit_queue(db, queue,
16573 new_revnum, new_date,
16574 new_author, scratch_pool),
16575 queue->wcroot);
16576
16577 return SVN_NO_ERROR;
16578 }
16579
16580 svn_error_t *
svn_wc__db_find_repos_node_in_wc(apr_array_header_t ** local_abspath_list,svn_wc__db_t * db,const char * wri_abspath,const char * repos_relpath,apr_pool_t * result_pool,apr_pool_t * scratch_pool)16581 svn_wc__db_find_repos_node_in_wc(apr_array_header_t **local_abspath_list,
16582 svn_wc__db_t *db,
16583 const char *wri_abspath,
16584 const char *repos_relpath,
16585 apr_pool_t *result_pool,
16586 apr_pool_t *scratch_pool)
16587 {
16588 svn_wc__db_wcroot_t *wcroot;
16589 const char *wri_relpath;
16590 svn_sqlite__stmt_t *stmt;
16591 svn_boolean_t have_row;
16592
16593 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
16594
16595 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &wri_relpath, db,
16596 wri_abspath, scratch_pool,
16597 scratch_pool));
16598 VERIFY_USABLE_WCROOT(wcroot);
16599
16600 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
16601 STMT_FIND_REPOS_PATH_IN_WC));
16602 SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, repos_relpath));
16603 SVN_ERR(svn_sqlite__step(&have_row, stmt));
16604
16605 *local_abspath_list = apr_array_make(result_pool, have_row ? 1 : 0,
16606 sizeof(const char*));
16607 while (have_row)
16608 {
16609 const char *local_relpath;
16610 const char *local_abspath;
16611
16612 local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
16613 local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
16614 result_pool);
16615 APR_ARRAY_PUSH(*local_abspath_list, const char *) = local_abspath;
16616
16617 SVN_ERR(svn_sqlite__step(&have_row, stmt));
16618 }
16619
16620 return svn_error_trace(svn_sqlite__reset(stmt));
16621 }
16622
16623 svn_error_t *
svn_wc__db_find_working_nodes_with_basename(apr_array_header_t ** local_abspaths,svn_wc__db_t * db,const char * wri_abspath,const char * basename,svn_node_kind_t kind,apr_pool_t * result_pool,apr_pool_t * scratch_pool)16624 svn_wc__db_find_working_nodes_with_basename(apr_array_header_t **local_abspaths,
16625 svn_wc__db_t *db,
16626 const char *wri_abspath,
16627 const char *basename,
16628 svn_node_kind_t kind,
16629 apr_pool_t *result_pool,
16630 apr_pool_t *scratch_pool)
16631 {
16632 svn_wc__db_wcroot_t *wcroot;
16633 const char *wri_relpath;
16634 svn_sqlite__stmt_t *stmt;
16635 svn_boolean_t have_row;
16636
16637 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
16638
16639 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &wri_relpath, db,
16640 wri_abspath, scratch_pool,
16641 scratch_pool));
16642 VERIFY_USABLE_WCROOT(wcroot);
16643
16644 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
16645 STMT_SELECT_PRESENT_HIGHEST_WORKING_NODES_BY_BASENAME_AND_KIND));
16646 SVN_ERR(svn_sqlite__bindf(stmt, "ist", wcroot->wc_id, basename,
16647 kind_map, kind));
16648 SVN_ERR(svn_sqlite__step(&have_row, stmt));
16649
16650 *local_abspaths = apr_array_make(result_pool, 1, sizeof(const char *));
16651
16652 while (have_row)
16653 {
16654 const char *local_relpath;
16655 const char *local_abspath;
16656
16657 local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
16658 local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
16659 result_pool);
16660 APR_ARRAY_PUSH(*local_abspaths, const char *) = local_abspath;
16661 SVN_ERR(svn_sqlite__step(&have_row, stmt));
16662 }
16663
16664 return svn_error_trace(svn_sqlite__reset(stmt));
16665 }
16666
16667 svn_error_t *
svn_wc__db_find_copies_of_repos_path(apr_array_header_t ** local_abspaths,svn_wc__db_t * db,const char * wri_abspath,const char * repos_relpath,svn_node_kind_t kind,apr_pool_t * result_pool,apr_pool_t * scratch_pool)16668 svn_wc__db_find_copies_of_repos_path(apr_array_header_t **local_abspaths,
16669 svn_wc__db_t *db,
16670 const char *wri_abspath,
16671 const char *repos_relpath,
16672 svn_node_kind_t kind,
16673 apr_pool_t *result_pool,
16674 apr_pool_t *scratch_pool)
16675 {
16676 svn_wc__db_wcroot_t *wcroot;
16677 const char *wri_relpath;
16678 svn_sqlite__stmt_t *stmt;
16679 svn_boolean_t have_row;
16680
16681 SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
16682
16683 SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &wri_relpath, db,
16684 wri_abspath, scratch_pool,
16685 scratch_pool));
16686 VERIFY_USABLE_WCROOT(wcroot);
16687
16688 SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
16689 STMT_SELECT_COPIES_OF_REPOS_RELPATH));
16690 SVN_ERR(svn_sqlite__bindf(stmt, "ist", wcroot->wc_id, repos_relpath,
16691 kind_map, kind));
16692 SVN_ERR(svn_sqlite__step(&have_row, stmt));
16693
16694 *local_abspaths = apr_array_make(result_pool, 1, sizeof(const char *));
16695
16696 while (have_row)
16697 {
16698 const char *local_relpath;
16699 const char *local_abspath;
16700
16701 local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
16702 local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
16703 result_pool);
16704 APR_ARRAY_PUSH(*local_abspaths, const char *) = local_abspath;
16705 SVN_ERR(svn_sqlite__step(&have_row, stmt));
16706 }
16707
16708 return svn_error_trace(svn_sqlite__reset(stmt));
16709 }
16710