]> CyberLeo.Net >> Repos - FreeBSD/releng/10.1.git/blob - contrib/subversion/subversion/libsvn_wc/wc_db.c
Copy stable/10@r272459 to releng/10.1 as part of
[FreeBSD/releng/10.1.git] / contrib / subversion / subversion / libsvn_wc / wc_db.c
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_types.h"
31 #include "svn_error.h"
32 #include "svn_dirent_uri.h"
33 #include "svn_path.h"
34 #include "svn_hash.h"
35 #include "svn_sorts.h"
36 #include "svn_wc.h"
37 #include "svn_checksum.h"
38 #include "svn_pools.h"
39
40 #include "wc.h"
41 #include "wc_db.h"
42 #include "adm_files.h"
43 #include "wc-queries.h"
44 #include "entries.h"
45 #include "lock.h"
46 #include "conflicts.h"
47 #include "wc_db_private.h"
48 #include "workqueue.h"
49 #include "token-map.h"
50
51 #include "svn_private_config.h"
52 #include "private/svn_sqlite.h"
53 #include "private/svn_skel.h"
54 #include "private/svn_wc_private.h"
55 #include "private/svn_token.h"
56
57
58 #define NOT_IMPLEMENTED() SVN__NOT_IMPLEMENTED()
59
60
61 /*
62  * Some filename constants.
63  */
64 #define SDB_FILE  "wc.db"
65
66 #define WCROOT_TEMPDIR_RELPATH   "tmp"
67
68
69 /*
70  * PARAMETER ASSERTIONS
71  *
72  * Every (semi-)public entrypoint in this file has a set of assertions on
73  * the parameters passed into the function. Since this is a brand new API,
74  * we want to make sure that everybody calls it properly. The original WC
75  * code had years to catch stray bugs, but we do not have that luxury in
76  * the wc-nb rewrite. Any extra assurances that we can find will be
77  * welcome. The asserts will ensure we have no doubt about the values
78  * passed into the function.
79  *
80  * Some parameters are *not* specifically asserted. Typically, these are
81  * params that will be used immediately, so something like a NULL value
82  * will be obvious.
83  *
84  * ### near 1.7 release, it would be a Good Thing to review the assertions
85  * ### and decide if any can be removed or switched to assert() in order
86  * ### to remove their runtime cost in the production release.
87  *
88  *
89  * DATABASE OPERATIONS
90  *
91  * Each function should leave the database in a consistent state. If it
92  * does *not*, then the implication is some other function needs to be
93  * called to restore consistency. Subtle requirements like that are hard
94  * to maintain over a long period of time, so this API will not allow it.
95  *
96  *
97  * STANDARD VARIABLE NAMES
98  *
99  * db     working copy database (this module)
100  * sdb    SQLite database (not to be confused with 'db')
101  * wc_id  a WCROOT id associated with a node
102  */
103
104 #define INVALID_REPOS_ID ((apr_int64_t) -1)
105 #define UNKNOWN_WC_ID ((apr_int64_t) -1)
106 #define FORMAT_FROM_SDB (-1)
107
108 /* Check if column number I, a property-skel column, contains a non-empty
109    set of properties. The empty set of properties is stored as "()", so we
110    have properties if the size of the column is larger than 2. */
111 #define SQLITE_PROPERTIES_AVAILABLE(stmt, i) \
112                  (svn_sqlite__column_bytes(stmt, i) > 2)
113
114 int
115 svn_wc__db_op_depth_for_upgrade(const char *local_relpath)
116 {
117   return relpath_depth(local_relpath);
118 }
119
120
121 /* Representation of a new base row for the NODES table */
122 typedef struct insert_base_baton_t {
123   /* common to all insertions into BASE */
124   svn_wc__db_status_t status;
125   svn_node_kind_t kind;
126   apr_int64_t repos_id;
127   const char *repos_relpath;
128   svn_revnum_t revision;
129
130   /* Only used when repos_id == INVALID_REPOS_ID */
131   const char *repos_root_url;
132   const char *repos_uuid;
133
134   /* common to all "normal" presence insertions */
135   const apr_hash_t *props;
136   svn_revnum_t changed_rev;
137   apr_time_t changed_date;
138   const char *changed_author;
139   const apr_hash_t *dav_cache;
140
141   /* for inserting directories */
142   const apr_array_header_t *children;
143   svn_depth_t depth;
144
145   /* for inserting files */
146   const svn_checksum_t *checksum;
147
148   /* for inserting symlinks */
149   const char *target;
150
151   svn_boolean_t file_external;
152
153   /* may need to insert/update ACTUAL to record a conflict  */
154   const svn_skel_t *conflict;
155
156   /* may need to insert/update ACTUAL to record new properties */
157   svn_boolean_t update_actual_props;
158   const apr_hash_t *new_actual_props;
159
160   /* A depth-first ordered array of svn_prop_inherited_item_t *
161      structures representing the properties inherited by the base
162      node. */
163   apr_array_header_t *iprops;
164
165   /* maybe we should copy information from a previous record? */
166   svn_boolean_t keep_recorded_info;
167
168   /* insert a base-deleted working node as well as a base node */
169   svn_boolean_t insert_base_deleted;
170
171   /* delete the current working nodes above BASE */
172   svn_boolean_t delete_working;
173
174   /* may have work items to queue in this transaction  */
175   const svn_skel_t *work_items;
176
177 } insert_base_baton_t;
178
179
180 /* Representation of a new working row for the NODES table */
181 typedef struct insert_working_baton_t {
182   /* common to all insertions into WORKING (including NODE_DATA) */
183   svn_wc__db_status_t presence;
184   svn_node_kind_t kind;
185   int op_depth;
186
187   /* common to all "normal" presence insertions */
188   const apr_hash_t *props;
189   svn_revnum_t changed_rev;
190   apr_time_t changed_date;
191   const char *changed_author;
192   apr_int64_t original_repos_id;
193   const char *original_repos_relpath;
194   svn_revnum_t original_revnum;
195   svn_boolean_t moved_here;
196
197   /* for inserting directories */
198   const apr_array_header_t *children;
199   svn_depth_t depth;
200
201   /* for inserting (copied/moved-here) files */
202   const svn_checksum_t *checksum;
203
204   /* for inserting symlinks */
205   const char *target;
206
207   svn_boolean_t update_actual_props;
208   const apr_hash_t *new_actual_props;
209
210   /* may have work items to queue in this transaction  */
211   const svn_skel_t *work_items;
212
213   /* may have conflict to install in this transaction */
214   const svn_skel_t *conflict;
215
216   /* If the value is > 0 and < op_depth, also insert a not-present
217      at op-depth NOT_PRESENT_OP_DEPTH, based on this same information */
218   int not_present_op_depth;
219
220 } insert_working_baton_t;
221
222 /* Representation of a new row for the EXTERNALS table */
223 typedef struct insert_external_baton_t {
224   /* common to all insertions into EXTERNALS */
225   svn_node_kind_t kind;
226   svn_wc__db_status_t presence;
227
228   /* The repository of the external */
229   apr_int64_t repos_id;
230   /* for file and symlink externals */
231   const char *repos_relpath;
232   svn_revnum_t revision;
233
234   /* Only used when repos_id == INVALID_REPOS_ID */
235   const char *repos_root_url;
236   const char *repos_uuid;
237
238   /* for file and symlink externals */
239   const apr_hash_t *props;
240   apr_array_header_t *iprops;
241   svn_revnum_t changed_rev;
242   apr_time_t changed_date;
243   const char *changed_author;
244   const apr_hash_t *dav_cache;
245
246   /* for inserting files */
247   const svn_checksum_t *checksum;
248
249   /* for inserting symlinks */
250   const char *target;
251
252   const char *record_ancestor_relpath;
253   const char *recorded_repos_relpath;
254   svn_revnum_t recorded_peg_revision;
255   svn_revnum_t recorded_revision;
256
257   /* may need to insert/update ACTUAL to record a conflict  */
258   const svn_skel_t *conflict;
259
260   /* may need to insert/update ACTUAL to record new properties */
261   svn_boolean_t update_actual_props;
262   const apr_hash_t *new_actual_props;
263
264   /* maybe we should copy information from a previous record? */
265   svn_boolean_t keep_recorded_info;
266
267   /* may have work items to queue in this transaction  */
268   const svn_skel_t *work_items;
269
270 } insert_external_baton_t;
271
272
273 /* Forward declarations  */
274 static svn_error_t *
275 add_work_items(svn_sqlite__db_t *sdb,
276                const svn_skel_t *skel,
277                apr_pool_t *scratch_pool);
278
279 static svn_error_t *
280 set_actual_props(apr_int64_t wc_id,
281                  const char *local_relpath,
282                  apr_hash_t *props,
283                  svn_sqlite__db_t *db,
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 wclock_owns_lock(svn_boolean_t *own_lock,
358                  svn_wc__db_wcroot_t *wcroot,
359                  const char *local_relpath,
360                  svn_boolean_t exact,
361                  apr_pool_t *scratch_pool);
362
363 static svn_error_t *
364 db_is_switched(svn_boolean_t *is_switched,
365                svn_node_kind_t *kind,
366                svn_wc__db_wcroot_t *wcroot,
367                const char *local_relpath,
368                apr_pool_t *scratch_pool);
369
370
371 /* Return the absolute path, in local path style, of LOCAL_RELPATH
372    in WCROOT.  */
373 static const char *
374 path_for_error_message(const svn_wc__db_wcroot_t *wcroot,
375                        const char *local_relpath,
376                        apr_pool_t *result_pool)
377 {
378   const char *local_abspath
379     = svn_dirent_join(wcroot->abspath, local_relpath, result_pool);
380
381   return svn_dirent_local_style(local_abspath, result_pool);
382 }
383
384
385 /* Return a file size from column SLOT of the SQLITE statement STMT, or
386    SVN_INVALID_FILESIZE if the column value is NULL.  */
387 static svn_filesize_t
388 get_recorded_size(svn_sqlite__stmt_t *stmt, int slot)
389 {
390   if (svn_sqlite__column_is_null(stmt, slot))
391     return SVN_INVALID_FILESIZE;
392   return svn_sqlite__column_int64(stmt, slot);
393 }
394
395
396 /* Return a lock info structure constructed from the given columns of the
397    SQLITE statement STMT, or return NULL if the token column value is null.  */
398 static svn_wc__db_lock_t *
399 lock_from_columns(svn_sqlite__stmt_t *stmt,
400                   int col_token,
401                   int col_owner,
402                   int col_comment,
403                   int col_date,
404                   apr_pool_t *result_pool)
405 {
406   svn_wc__db_lock_t *lock;
407
408   if (svn_sqlite__column_is_null(stmt, col_token))
409     {
410       lock = NULL;
411     }
412   else
413     {
414       lock = apr_pcalloc(result_pool, sizeof(svn_wc__db_lock_t));
415       lock->token = svn_sqlite__column_text(stmt, col_token, result_pool);
416       lock->owner = svn_sqlite__column_text(stmt, col_owner, result_pool);
417       lock->comment = svn_sqlite__column_text(stmt, col_comment, result_pool);
418       lock->date = svn_sqlite__column_int64(stmt, col_date);
419     }
420   return lock;
421 }
422
423
424 svn_error_t *
425 svn_wc__db_fetch_repos_info(const char **repos_root_url,
426                             const char **repos_uuid,
427                             svn_sqlite__db_t *sdb,
428                             apr_int64_t repos_id,
429                             apr_pool_t *result_pool)
430 {
431   svn_sqlite__stmt_t *stmt;
432   svn_boolean_t have_row;
433
434   if (!repos_root_url && !repos_uuid)
435     return SVN_NO_ERROR;
436
437   if (repos_id == INVALID_REPOS_ID)
438     {
439       if (repos_root_url)
440         *repos_root_url = NULL;
441       if (repos_uuid)
442         *repos_uuid = NULL;
443       return SVN_NO_ERROR;
444     }
445
446   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
447                                     STMT_SELECT_REPOSITORY_BY_ID));
448   SVN_ERR(svn_sqlite__bindf(stmt, "i", repos_id));
449   SVN_ERR(svn_sqlite__step(&have_row, stmt));
450   if (!have_row)
451     return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
452                              _("No REPOSITORY table entry for id '%ld'"),
453                              (long int)repos_id);
454
455   if (repos_root_url)
456     *repos_root_url = svn_sqlite__column_text(stmt, 0, result_pool);
457   if (repos_uuid)
458     *repos_uuid = svn_sqlite__column_text(stmt, 1, result_pool);
459
460   return svn_error_trace(svn_sqlite__reset(stmt));
461 }
462
463 /* Set *REPOS_ID, *REVISION and *REPOS_RELPATH from the given columns of the
464    SQLITE statement STMT, or to NULL/SVN_INVALID_REVNUM if the respective
465    column value is null.  Any of the output parameters may be NULL if not
466    required.  */
467 static void
468 repos_location_from_columns(apr_int64_t *repos_id,
469                             svn_revnum_t *revision,
470                             const char **repos_relpath,
471                             svn_sqlite__stmt_t *stmt,
472                             int col_repos_id,
473                             int col_revision,
474                             int col_repos_relpath,
475                             apr_pool_t *result_pool)
476 {
477   if (repos_id)
478     {
479       /* Fetch repository information via REPOS_ID. */
480       if (svn_sqlite__column_is_null(stmt, col_repos_id))
481         *repos_id = INVALID_REPOS_ID;
482       else
483         *repos_id = svn_sqlite__column_int64(stmt, col_repos_id);
484     }
485   if (revision)
486     {
487       *revision = svn_sqlite__column_revnum(stmt, col_revision);
488     }
489   if (repos_relpath)
490     {
491       *repos_relpath = svn_sqlite__column_text(stmt, col_repos_relpath,
492                                                result_pool);
493     }
494 }
495
496
497 /* Get the statement given by STMT_IDX, and bind the appropriate wc_id and
498    local_relpath based upon LOCAL_ABSPATH.  Store it in *STMT, and use
499    SCRATCH_POOL for temporary allocations.
500
501    Note: WC_ID and LOCAL_RELPATH must be arguments 1 and 2 in the statement. */
502 static svn_error_t *
503 get_statement_for_path(svn_sqlite__stmt_t **stmt,
504                        svn_wc__db_t *db,
505                        const char *local_abspath,
506                        int stmt_idx,
507                        apr_pool_t *scratch_pool)
508 {
509   svn_wc__db_wcroot_t *wcroot;
510   const char *local_relpath;
511
512   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
513
514   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
515                               local_abspath, scratch_pool, scratch_pool));
516   VERIFY_USABLE_WCROOT(wcroot);
517
518   SVN_ERR(svn_sqlite__get_statement(stmt, wcroot->sdb, stmt_idx));
519   SVN_ERR(svn_sqlite__bindf(*stmt, "is", wcroot->wc_id, local_relpath));
520
521   return SVN_NO_ERROR;
522 }
523
524
525 /* For a given REPOS_ROOT_URL/REPOS_UUID pair, return the existing REPOS_ID
526    value. If one does not exist, then create a new one. */
527 static svn_error_t *
528 create_repos_id(apr_int64_t *repos_id,
529                 const char *repos_root_url,
530                 const char *repos_uuid,
531                 svn_sqlite__db_t *sdb,
532                 apr_pool_t *scratch_pool)
533 {
534   svn_sqlite__stmt_t *get_stmt;
535   svn_sqlite__stmt_t *insert_stmt;
536   svn_boolean_t have_row;
537
538   SVN_ERR(svn_sqlite__get_statement(&get_stmt, sdb, STMT_SELECT_REPOSITORY));
539   SVN_ERR(svn_sqlite__bindf(get_stmt, "s", repos_root_url));
540   SVN_ERR(svn_sqlite__step(&have_row, get_stmt));
541
542   if (have_row)
543     {
544       *repos_id = svn_sqlite__column_int64(get_stmt, 0);
545       return svn_error_trace(svn_sqlite__reset(get_stmt));
546     }
547   SVN_ERR(svn_sqlite__reset(get_stmt));
548
549   /* NOTE: strictly speaking, there is a race condition between the
550      above query and the insertion below. We're simply going to ignore
551      that, as it means two processes are *modifying* the working copy
552      at the same time, *and* new repositores are becoming visible.
553      This is rare enough, let alone the miniscule chance of hitting
554      this race condition. Further, simply failing out will leave the
555      database in a consistent state, and the user can just re-run the
556      failed operation. */
557
558   SVN_ERR(svn_sqlite__get_statement(&insert_stmt, sdb,
559                                     STMT_INSERT_REPOSITORY));
560   SVN_ERR(svn_sqlite__bindf(insert_stmt, "ss", repos_root_url, repos_uuid));
561   return svn_error_trace(svn_sqlite__insert(repos_id, insert_stmt));
562 }
563
564
565 /* Initialize the baton with appropriate "blank" values. This allows the
566    insertion function to leave certain columns null.  */
567 static void
568 blank_ibb(insert_base_baton_t *pibb)
569 {
570   memset(pibb, 0, sizeof(*pibb));
571   pibb->revision = SVN_INVALID_REVNUM;
572   pibb->changed_rev = SVN_INVALID_REVNUM;
573   pibb->depth = svn_depth_infinity;
574   pibb->repos_id = INVALID_REPOS_ID;
575 }
576
577
578 svn_error_t *
579 svn_wc__db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot,
580                                 const char *local_relpath,
581                                 svn_node_kind_t kind,
582                                 int op_depth,
583                                 apr_pool_t *scratch_pool)
584 {
585   svn_boolean_t have_row;
586   svn_sqlite__stmt_t *stmt;
587   int parent_op_depth;
588   const char *parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
589
590   SVN_ERR_ASSERT(local_relpath[0]);
591
592   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
593                                     STMT_SELECT_LOWEST_WORKING_NODE));
594   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, parent_relpath,
595                             op_depth));
596   SVN_ERR(svn_sqlite__step(&have_row, stmt));
597   if (have_row)
598     parent_op_depth = svn_sqlite__column_int(stmt, 0);
599   SVN_ERR(svn_sqlite__reset(stmt));
600   if (have_row)
601     {
602       int existing_op_depth;
603
604       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
605                                 op_depth));
606       SVN_ERR(svn_sqlite__step(&have_row, stmt));
607       if (have_row)
608         existing_op_depth = svn_sqlite__column_int(stmt, 0);
609       SVN_ERR(svn_sqlite__reset(stmt));
610       if (!have_row || parent_op_depth < existing_op_depth)
611         {
612           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
613                               STMT_INSTALL_WORKING_NODE_FOR_DELETE));
614           SVN_ERR(svn_sqlite__bindf(stmt, "isdst", wcroot->wc_id,
615                                     local_relpath, parent_op_depth,
616                                     parent_relpath, kind_map, kind));
617           SVN_ERR(svn_sqlite__update(NULL, stmt));
618         }
619     }
620
621   return SVN_NO_ERROR;
622 }
623
624
625 /* This is the reverse of svn_wc__db_extend_parent_delete.
626
627    When removing a node if the parent has a higher working node then
628    the parent node and this node are both deleted or replaced and any
629    delete over this node must be removed.
630
631    This function (like most wcroot functions) assumes that its caller
632    only uses this function within an sqlite transaction if atomic
633    behavior is needed.
634  */
635 svn_error_t *
636 svn_wc__db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot,
637                                  const char *local_relpath,
638                                  int op_depth,
639                                  apr_pool_t *scratch_pool)
640 {
641   svn_sqlite__stmt_t *stmt;
642   svn_boolean_t have_row;
643   int working_depth;
644   svn_wc__db_status_t presence;
645   const char *moved_to;
646
647   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
648                                     STMT_SELECT_LOWEST_WORKING_NODE));
649   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
650                             op_depth));
651   SVN_ERR(svn_sqlite__step(&have_row, stmt));
652
653   if (!have_row)
654     return svn_error_trace(svn_sqlite__reset(stmt));
655
656   working_depth = svn_sqlite__column_int(stmt, 0);
657   presence = svn_sqlite__column_token(stmt, 1, presence_map);
658   moved_to = svn_sqlite__column_text(stmt, 3, scratch_pool);
659
660   SVN_ERR(svn_sqlite__reset(stmt));
661
662   if (moved_to)
663     {
664       /* Turn the move into a copy to keep the NODES table valid */
665       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
666                                         STMT_CLEAR_MOVED_HERE_RECURSIVE));
667       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
668                                 moved_to, relpath_depth(moved_to)));
669       SVN_ERR(svn_sqlite__step_done(stmt));
670
671       /* This leaves just the moved_to information on the origin,
672          which we will remove in the next step */
673     }
674
675   if (presence == svn_wc__db_status_base_deleted)
676     {
677       /* Nothing left to shadow; remove the base-deleted node */
678       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_NODE));
679     }
680   else if (moved_to)
681     {
682       /* Clear moved to information, as this node is no longer base-deleted */
683       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
684                                         STMT_CLEAR_MOVED_TO_RELPATH));
685       }
686   else
687     {
688       /* Nothing to update */
689       return SVN_NO_ERROR;
690     }
691
692   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
693                             working_depth));
694
695   return svn_error_trace(svn_sqlite__update(NULL, stmt));
696 }
697
698
699
700 /* Insert the base row represented by (insert_base_baton_t *) BATON. */
701 static svn_error_t *
702 insert_base_node(const insert_base_baton_t *pibb,
703                  svn_wc__db_wcroot_t *wcroot,
704                  const char *local_relpath,
705                  apr_pool_t *scratch_pool)
706 {
707   apr_int64_t repos_id = pibb->repos_id;
708   svn_sqlite__stmt_t *stmt;
709   svn_filesize_t recorded_size = SVN_INVALID_FILESIZE;
710   apr_int64_t recorded_time;
711
712   /* The directory at the WCROOT has a NULL parent_relpath. Otherwise,
713      bind the appropriate parent_relpath. */
714   const char *parent_relpath =
715     (*local_relpath == '\0') ? NULL
716     : svn_relpath_dirname(local_relpath, scratch_pool);
717
718   if (pibb->repos_id == INVALID_REPOS_ID)
719     SVN_ERR(create_repos_id(&repos_id, pibb->repos_root_url, pibb->repos_uuid,
720                             wcroot->sdb, scratch_pool));
721
722   SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
723   SVN_ERR_ASSERT(pibb->repos_relpath != NULL);
724
725   if (pibb->keep_recorded_info)
726     {
727       svn_boolean_t have_row;
728       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
729                                         STMT_SELECT_BASE_NODE));
730       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
731       SVN_ERR(svn_sqlite__step(&have_row, stmt));
732       if (have_row)
733         {
734           /* Preserve size and modification time if caller asked us to. */
735           recorded_size = get_recorded_size(stmt, 6);
736           recorded_time = svn_sqlite__column_int64(stmt, 12);
737         }
738       SVN_ERR(svn_sqlite__reset(stmt));
739     }
740
741   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
742   SVN_ERR(svn_sqlite__bindf(stmt, "isdsisr"
743                             "tstr"               /* 8 - 11 */
744                             "isnnnnns",          /* 12 - 19 */
745                             wcroot->wc_id,       /* 1 */
746                             local_relpath,       /* 2 */
747                             0,              /* op_depth is 0 for base */
748                             parent_relpath,      /* 4 */
749                             repos_id,
750                             pibb->repos_relpath,
751                             pibb->revision,
752                             presence_map, pibb->status, /* 8 */
753                             (pibb->kind == svn_node_dir) ? /* 9 */
754                              svn_token__to_word(depth_map, pibb->depth) : NULL,
755                             kind_map, pibb->kind, /* 10 */
756                             pibb->changed_rev,    /* 11 */
757                             pibb->changed_date,   /* 12 */
758                             pibb->changed_author, /* 13 */
759                             (pibb->kind == svn_node_symlink) ?
760                                 pibb->target : NULL)); /* 19 */
761   if (pibb->kind == svn_node_file)
762     {
763       if (!pibb->checksum
764           && pibb->status != svn_wc__db_status_not_present
765           && pibb->status != svn_wc__db_status_excluded
766           && pibb->status != svn_wc__db_status_server_excluded)
767         return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
768                                  _("The file '%s' has no checksum."),
769                                  path_for_error_message(wcroot, local_relpath,
770                                                         scratch_pool));
771
772       SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, pibb->checksum,
773                                         scratch_pool));
774
775       if (recorded_size != SVN_INVALID_FILESIZE)
776         {
777           SVN_ERR(svn_sqlite__bind_int64(stmt, 16, recorded_size));
778           SVN_ERR(svn_sqlite__bind_int64(stmt, 17, recorded_time));
779         }
780     }
781
782   /* Set properties.  Must be null if presence not normal or incomplete. */
783   assert(pibb->status == svn_wc__db_status_normal
784          || pibb->status == svn_wc__db_status_incomplete
785          || pibb->props == NULL);
786   SVN_ERR(svn_sqlite__bind_properties(stmt, 15, pibb->props,
787                                       scratch_pool));
788
789   SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, pibb->iprops,
790                                       scratch_pool));
791
792   if (pibb->dav_cache)
793     SVN_ERR(svn_sqlite__bind_properties(stmt, 18, pibb->dav_cache,
794                                         scratch_pool));
795
796   if (pibb->file_external)
797     SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
798
799   SVN_ERR(svn_sqlite__insert(NULL, stmt));
800
801   if (pibb->update_actual_props)
802     {
803       /* Cast away const, to allow calling property helpers */
804       apr_hash_t *base_props = (apr_hash_t *)pibb->props;
805       apr_hash_t *new_actual_props = (apr_hash_t *)pibb->new_actual_props;
806
807       if (base_props != NULL
808           && new_actual_props != NULL
809           && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
810         {
811           apr_array_header_t *diffs;
812
813           SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
814                                  scratch_pool));
815
816           if (diffs->nelts == 0)
817             new_actual_props = NULL;
818         }
819
820       SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props,
821                                wcroot->sdb, scratch_pool));
822     }
823
824   if (pibb->kind == svn_node_dir && pibb->children)
825     SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
826                                        local_relpath,
827                                        repos_id,
828                                        pibb->repos_relpath,
829                                        pibb->revision,
830                                        pibb->children,
831                                        0 /* BASE */,
832                                        scratch_pool));
833
834   /* When this is not the root node, check shadowing behavior */
835   if (*local_relpath)
836     {
837       if (parent_relpath
838           && ((pibb->status == svn_wc__db_status_normal)
839               || (pibb->status == svn_wc__db_status_incomplete))
840           && ! pibb->file_external)
841         {
842           SVN_ERR(svn_wc__db_extend_parent_delete(wcroot, local_relpath,
843                                                   pibb->kind, 0,
844                                                   scratch_pool));
845         }
846       else if (pibb->status == svn_wc__db_status_not_present
847                || pibb->status == svn_wc__db_status_server_excluded
848                || pibb->status == svn_wc__db_status_excluded)
849         {
850           SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
851                                                    scratch_pool));
852         }
853     }
854
855   if (pibb->delete_working)
856     {
857       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
858                                     STMT_DELETE_WORKING_NODE));
859       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
860       SVN_ERR(svn_sqlite__step_done(stmt));
861     }
862   if (pibb->insert_base_deleted)
863     {
864       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
865                                         STMT_INSERT_DELETE_FROM_BASE));
866       SVN_ERR(svn_sqlite__bindf(stmt, "isd",
867                                 wcroot->wc_id, local_relpath,
868                                 relpath_depth(local_relpath)));
869       SVN_ERR(svn_sqlite__step_done(stmt));
870     }
871
872   SVN_ERR(add_work_items(wcroot->sdb, pibb->work_items, scratch_pool));
873   if (pibb->conflict)
874     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
875                                               pibb->conflict, scratch_pool));
876
877   return SVN_NO_ERROR;
878 }
879
880
881 /* Initialize the baton with appropriate "blank" values. This allows the
882    insertion function to leave certain columns null.  */
883 static void
884 blank_iwb(insert_working_baton_t *piwb)
885 {
886   memset(piwb, 0, sizeof(*piwb));
887   piwb->changed_rev = SVN_INVALID_REVNUM;
888   piwb->depth = svn_depth_infinity;
889
890   /* ORIGINAL_REPOS_ID and ORIGINAL_REVNUM could use some kind of "nil"
891      value, but... meh. We'll avoid them if ORIGINAL_REPOS_RELPATH==NULL.  */
892 }
893
894
895 /* Insert a row in NODES for each (const char *) child name in CHILDREN,
896    whose parent directory is LOCAL_RELPATH, at op_depth=OP_DEPTH.  Set each
897    child's presence to 'incomplete', kind to 'unknown', repos_id to REPOS_ID,
898    repos_path by appending the child name to REPOS_PATH, and revision to
899    REVISION (which should match the parent's revision).
900
901    If REPOS_ID is INVALID_REPOS_ID, set each child's repos_id to null. */
902 static svn_error_t *
903 insert_incomplete_children(svn_sqlite__db_t *sdb,
904                            apr_int64_t wc_id,
905                            const char *local_relpath,
906                            apr_int64_t repos_id,
907                            const char *repos_path,
908                            svn_revnum_t revision,
909                            const apr_array_header_t *children,
910                            int op_depth,
911                            apr_pool_t *scratch_pool)
912 {
913   svn_sqlite__stmt_t *stmt;
914   int i;
915   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
916   apr_hash_t *moved_to_relpaths = apr_hash_make(scratch_pool);
917
918   SVN_ERR_ASSERT(repos_path != NULL || op_depth > 0);
919   SVN_ERR_ASSERT((repos_id != INVALID_REPOS_ID)
920                  == (repos_path != NULL));
921
922   /* If we're inserting WORKING nodes, we might be replacing existing
923    * nodes which were moved-away. We need to retain the moved-to relpath of
924    * such nodes in order not to lose move information during replace. */
925   if (op_depth > 0)
926     {
927       for (i = children->nelts; i--; )
928         {
929           const char *name = APR_ARRAY_IDX(children, i, const char *);
930           svn_boolean_t have_row;
931
932           svn_pool_clear(iterpool);
933
934           SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
935                                             STMT_SELECT_WORKING_NODE));
936           SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id,
937                                     svn_relpath_join(local_relpath, name,
938                                                      iterpool)));
939           SVN_ERR(svn_sqlite__step(&have_row, stmt));
940           if (have_row && !svn_sqlite__column_is_null(stmt, 14))
941             svn_hash_sets(moved_to_relpaths, name,
942                           svn_sqlite__column_text(stmt, 14, scratch_pool));
943
944           SVN_ERR(svn_sqlite__reset(stmt));
945         }
946     }
947
948   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
949
950   for (i = children->nelts; i--; )
951     {
952       const char *name = APR_ARRAY_IDX(children, i, const char *);
953
954       svn_pool_clear(iterpool);
955
956       SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnrsnsnnnnnnnnnnsn",
957                                 wc_id,
958                                 svn_relpath_join(local_relpath, name,
959                                                  iterpool),
960                                 op_depth,
961                                 local_relpath,
962                                 revision,
963                                 "incomplete", /* 8, presence */
964                                 "unknown",    /* 10, kind */
965                                 /* 21, moved_to */
966                                 svn_hash_gets(moved_to_relpaths, name)));
967       if (repos_id != INVALID_REPOS_ID)
968         {
969           SVN_ERR(svn_sqlite__bind_int64(stmt, 5, repos_id));
970           SVN_ERR(svn_sqlite__bind_text(stmt, 6,
971                                         svn_relpath_join(repos_path, name,
972                                                          iterpool)));
973         }
974
975       SVN_ERR(svn_sqlite__insert(NULL, stmt));
976     }
977
978   svn_pool_destroy(iterpool);
979
980   return SVN_NO_ERROR;
981 }
982
983
984 /* Insert the working row represented by (insert_working_baton_t *) BATON. */
985 static svn_error_t *
986 insert_working_node(const insert_working_baton_t *piwb,
987                     svn_wc__db_wcroot_t *wcroot,
988                     const char *local_relpath,
989                     apr_pool_t *scratch_pool)
990 {
991   const char *parent_relpath;
992   const char *moved_to_relpath = NULL;
993   svn_sqlite__stmt_t *stmt;
994   svn_boolean_t have_row;
995
996   SVN_ERR_ASSERT(piwb->op_depth > 0);
997
998   /* We cannot insert a WORKING_NODE row at the wcroot.  */
999   SVN_ERR_ASSERT(*local_relpath != '\0');
1000   parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
1001
1002   /* Preserve existing moved-to information for this relpath,
1003    * which might exist in case we're replacing an existing base-deleted
1004    * node. */
1005   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
1006   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1007                             piwb->op_depth));
1008   SVN_ERR(svn_sqlite__step(&have_row, stmt));
1009   if (have_row)
1010     moved_to_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
1011   SVN_ERR(svn_sqlite__reset(stmt));
1012
1013   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
1014   SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnntstrisn"
1015                 "nnnn" /* properties translated_size last_mod_time dav_cache */
1016                 "sns", /* symlink_target, file_external, moved_to */
1017                 wcroot->wc_id, local_relpath,
1018                 piwb->op_depth,
1019                 parent_relpath,
1020                 presence_map, piwb->presence,
1021                 (piwb->kind == svn_node_dir)
1022                             ? svn_token__to_word(depth_map, piwb->depth) : NULL,
1023                 kind_map, piwb->kind,
1024                 piwb->changed_rev,
1025                 piwb->changed_date,
1026                 piwb->changed_author,
1027                 /* Note: incomplete nodes may have a NULL target.  */
1028                 (piwb->kind == svn_node_symlink)
1029                             ? piwb->target : NULL,
1030                 moved_to_relpath));
1031
1032   if (piwb->moved_here)
1033     {
1034       SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
1035     }
1036
1037   if (piwb->kind == svn_node_file)
1038     {
1039       SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, piwb->checksum,
1040                                         scratch_pool));
1041     }
1042
1043   if (piwb->original_repos_relpath != NULL)
1044     {
1045       SVN_ERR(svn_sqlite__bind_int64(stmt, 5, piwb->original_repos_id));
1046       SVN_ERR(svn_sqlite__bind_text(stmt, 6, piwb->original_repos_relpath));
1047       SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, piwb->original_revnum));
1048     }
1049
1050   /* Set properties.  Must be null if presence not normal or incomplete. */
1051   assert(piwb->presence == svn_wc__db_status_normal
1052          || piwb->presence == svn_wc__db_status_incomplete
1053          || piwb->props == NULL);
1054   SVN_ERR(svn_sqlite__bind_properties(stmt, 15, piwb->props, scratch_pool));
1055
1056   SVN_ERR(svn_sqlite__insert(NULL, stmt));
1057
1058   /* Insert incomplete children, if specified.
1059      The children are part of the same op and so have the same op_depth.
1060      (The only time we'd want a different depth is during a recursive
1061      simple add, but we never insert children here during a simple add.) */
1062   if (piwb->kind == svn_node_dir && piwb->children)
1063     SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
1064                                        local_relpath,
1065                                        INVALID_REPOS_ID /* inherit repos_id */,
1066                                        NULL /* inherit repos_path */,
1067                                        piwb->original_revnum,
1068                                        piwb->children,
1069                                        piwb->op_depth,
1070                                        scratch_pool));
1071
1072   if (piwb->update_actual_props)
1073     {
1074       /* Cast away const, to allow calling property helpers */
1075       apr_hash_t *base_props = (apr_hash_t *)piwb->props;
1076       apr_hash_t *new_actual_props = (apr_hash_t *)piwb->new_actual_props;
1077
1078       if (base_props != NULL
1079           && new_actual_props != NULL
1080           && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
1081         {
1082           apr_array_header_t *diffs;
1083
1084           SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
1085                                  scratch_pool));
1086
1087           if (diffs->nelts == 0)
1088             new_actual_props = NULL;
1089         }
1090
1091       SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath, new_actual_props,
1092                                wcroot->sdb, scratch_pool));
1093     }
1094
1095   if (piwb->kind == svn_node_dir)
1096     {
1097       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1098                                         STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST));
1099       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1100       SVN_ERR(svn_sqlite__step_done(stmt));
1101
1102       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1103                                         STMT_DELETE_ACTUAL_EMPTY));
1104       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1105       SVN_ERR(svn_sqlite__step_done(stmt));
1106     }
1107
1108   if (piwb->not_present_op_depth > 0
1109       && piwb->not_present_op_depth < piwb->op_depth)
1110     {
1111       /* And also insert a not-present node to tell the commit processing that
1112          a child of the parent node was not copied. */
1113       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1114                                         STMT_INSERT_NODE));
1115
1116       SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
1117                                 wcroot->wc_id, local_relpath,
1118                                 piwb->not_present_op_depth, parent_relpath,
1119                                 piwb->original_repos_id,
1120                                 piwb->original_repos_relpath,
1121                                 piwb->original_revnum,
1122                                 presence_map, svn_wc__db_status_not_present,
1123                                 /* NULL */
1124                                 kind_map, piwb->kind));
1125
1126       SVN_ERR(svn_sqlite__step_done(stmt));
1127     }
1128
1129   SVN_ERR(add_work_items(wcroot->sdb, piwb->work_items, scratch_pool));
1130   if (piwb->conflict)
1131     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
1132                                               piwb->conflict, scratch_pool));
1133
1134   return SVN_NO_ERROR;
1135 }
1136
1137
1138 /* Each name is allocated in RESULT_POOL and stored into CHILDREN as a key
1139    pointed to the same name.  */
1140 static svn_error_t *
1141 add_children_to_hash(apr_hash_t *children,
1142                      int stmt_idx,
1143                      svn_sqlite__db_t *sdb,
1144                      apr_int64_t wc_id,
1145                      const char *parent_relpath,
1146                      apr_pool_t *result_pool)
1147 {
1148   svn_sqlite__stmt_t *stmt;
1149   svn_boolean_t have_row;
1150
1151   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, stmt_idx));
1152   SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, parent_relpath));
1153   SVN_ERR(svn_sqlite__step(&have_row, stmt));
1154   while (have_row)
1155     {
1156       const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1157       const char *name = svn_relpath_basename(child_relpath, result_pool);
1158
1159       svn_hash_sets(children, name, name);
1160
1161       SVN_ERR(svn_sqlite__step(&have_row, stmt));
1162     }
1163
1164   return svn_sqlite__reset(stmt);
1165 }
1166
1167
1168 /* Set *CHILDREN to a new array of the (const char *) basenames of the
1169    immediate children, whatever their status, of the working node at
1170    LOCAL_RELPATH. */
1171 static svn_error_t *
1172 gather_children2(const apr_array_header_t **children,
1173                  svn_wc__db_wcroot_t *wcroot,
1174                  const char *local_relpath,
1175                  apr_pool_t *result_pool,
1176                  apr_pool_t *scratch_pool)
1177 {
1178   apr_hash_t *names_hash = apr_hash_make(scratch_pool);
1179   apr_array_header_t *names_array;
1180
1181   /* All of the names get allocated in RESULT_POOL.  It
1182      appears to be faster to use the hash to remove duplicates than to
1183      use DISTINCT in the SQL query. */
1184   SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_WORKING_CHILDREN,
1185                                wcroot->sdb, wcroot->wc_id,
1186                                local_relpath, result_pool));
1187
1188   SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool));
1189   *children = names_array;
1190   return SVN_NO_ERROR;
1191 }
1192
1193 /* Return in *CHILDREN all of the children of the directory LOCAL_RELPATH,
1194    of any status, in all op-depths in the NODES table. */
1195 static svn_error_t *
1196 gather_children(const apr_array_header_t **children,
1197                 svn_wc__db_wcroot_t *wcroot,
1198                 const char *local_relpath,
1199                 apr_pool_t *result_pool,
1200                 apr_pool_t *scratch_pool)
1201 {
1202   apr_hash_t *names_hash = apr_hash_make(scratch_pool);
1203   apr_array_header_t *names_array;
1204
1205   /* All of the names get allocated in RESULT_POOL.  It
1206      appears to be faster to use the hash to remove duplicates than to
1207      use DISTINCT in the SQL query. */
1208   SVN_ERR(add_children_to_hash(names_hash, STMT_SELECT_NODE_CHILDREN,
1209                                wcroot->sdb, wcroot->wc_id,
1210                                local_relpath, result_pool));
1211
1212   SVN_ERR(svn_hash_keys(&names_array, names_hash, result_pool));
1213   *children = names_array;
1214   return SVN_NO_ERROR;
1215 }
1216
1217
1218 /* Set *CHILDREN to a new array of (const char *) names of the children of
1219    the repository directory corresponding to WCROOT:LOCAL_RELPATH:OP_DEPTH -
1220    that is, only the children that are at the same op-depth as their parent. */
1221 static svn_error_t *
1222 gather_repo_children(const apr_array_header_t **children,
1223                      svn_wc__db_wcroot_t *wcroot,
1224                      const char *local_relpath,
1225                      int op_depth,
1226                      apr_pool_t *result_pool,
1227                      apr_pool_t *scratch_pool)
1228 {
1229   apr_array_header_t *result
1230     = apr_array_make(result_pool, 0, sizeof(const char *));
1231   svn_sqlite__stmt_t *stmt;
1232   svn_boolean_t have_row;
1233
1234   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1235                                     STMT_SELECT_OP_DEPTH_CHILDREN));
1236   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1237                             op_depth));
1238   SVN_ERR(svn_sqlite__step(&have_row, stmt));
1239   while (have_row)
1240     {
1241       const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1242
1243       /* Allocate the name in RESULT_POOL so we won't have to copy it. */
1244       APR_ARRAY_PUSH(result, const char *)
1245         = svn_relpath_basename(child_relpath, result_pool);
1246
1247       SVN_ERR(svn_sqlite__step(&have_row, stmt));
1248     }
1249   SVN_ERR(svn_sqlite__reset(stmt));
1250
1251   *children = result;
1252   return SVN_NO_ERROR;
1253 }
1254
1255 svn_error_t *
1256 svn_wc__db_get_children_op_depth(apr_hash_t **children,
1257                                  svn_wc__db_wcroot_t *wcroot,
1258                                  const char *local_relpath,
1259                                  int op_depth,
1260                                  apr_pool_t *result_pool,
1261                                  apr_pool_t *scratch_pool)
1262 {
1263   svn_sqlite__stmt_t *stmt;
1264   svn_boolean_t have_row;
1265
1266   *children = apr_hash_make(result_pool);
1267
1268   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1269                                     STMT_SELECT_OP_DEPTH_CHILDREN));
1270   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1271                             op_depth));
1272   SVN_ERR(svn_sqlite__step(&have_row, stmt));
1273   while (have_row)
1274     {
1275       const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1276       svn_node_kind_t *child_kind = apr_palloc(result_pool, sizeof(svn_node_kind_t));
1277
1278       *child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
1279       svn_hash_sets(*children,
1280                     svn_relpath_basename(child_relpath, result_pool),
1281                     child_kind);
1282
1283       SVN_ERR(svn_sqlite__step(&have_row, stmt));
1284     }
1285   SVN_ERR(svn_sqlite__reset(stmt));
1286
1287   return SVN_NO_ERROR;
1288 }
1289
1290
1291 /* Return TRUE if CHILD_ABSPATH is an immediate child of PARENT_ABSPATH.
1292  * Else, return FALSE. */
1293 static svn_boolean_t
1294 is_immediate_child_path(const char *parent_abspath, const char *child_abspath)
1295 {
1296   const char *local_relpath = svn_dirent_skip_ancestor(parent_abspath,
1297                                                        child_abspath);
1298
1299   /* To be an immediate child local_relpath should have one (not empty)
1300      component */
1301   return local_relpath && *local_relpath && !strchr(local_relpath, '/');
1302 }
1303
1304
1305 /* Remove the access baton for LOCAL_ABSPATH from ACCESS_CACHE. */
1306 static void
1307 remove_from_access_cache(apr_hash_t *access_cache,
1308                          const char *local_abspath)
1309 {
1310   svn_wc_adm_access_t *adm_access;
1311
1312   adm_access = svn_hash_gets(access_cache, local_abspath);
1313   if (adm_access)
1314     svn_wc__adm_access_set_entries(adm_access, NULL);
1315 }
1316
1317
1318 /* Flush the access baton for LOCAL_ABSPATH, and any of its children up to
1319  * the specified DEPTH, from the access baton cache in WCROOT.
1320  * Also flush the access baton for the parent of LOCAL_ABSPATH.I
1321  *
1322  * This function must be called when the access baton cache goes stale,
1323  * i.e. data about LOCAL_ABSPATH will need to be read again from disk.
1324  *
1325  * Use SCRATCH_POOL for temporary allocations. */
1326 static svn_error_t *
1327 flush_entries(svn_wc__db_wcroot_t *wcroot,
1328               const char *local_abspath,
1329               svn_depth_t depth,
1330               apr_pool_t *scratch_pool)
1331 {
1332   const char *parent_abspath;
1333
1334   if (apr_hash_count(wcroot->access_cache) == 0)
1335     return SVN_NO_ERROR;
1336
1337   remove_from_access_cache(wcroot->access_cache, local_abspath);
1338
1339   if (depth > svn_depth_empty)
1340     {
1341       apr_hash_index_t *hi;
1342
1343       /* Flush access batons of children within the specified depth. */
1344       for (hi = apr_hash_first(scratch_pool, wcroot->access_cache);
1345            hi;
1346            hi = apr_hash_next(hi))
1347         {
1348           const char *item_abspath = svn__apr_hash_index_key(hi);
1349
1350           if ((depth == svn_depth_files || depth == svn_depth_immediates) &&
1351               is_immediate_child_path(local_abspath, item_abspath))
1352             {
1353               remove_from_access_cache(wcroot->access_cache, item_abspath);
1354             }
1355           else if (depth == svn_depth_infinity &&
1356                    svn_dirent_is_ancestor(local_abspath, item_abspath))
1357             {
1358               remove_from_access_cache(wcroot->access_cache, item_abspath);
1359             }
1360         }
1361     }
1362
1363   /* We're going to be overly aggressive here and just flush the parent
1364      without doing much checking.  This may hurt performance for
1365      legacy API consumers, but that's not our problem. :) */
1366   parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
1367   remove_from_access_cache(wcroot->access_cache, parent_abspath);
1368
1369   return SVN_NO_ERROR;
1370 }
1371
1372
1373 /* Add a single WORK_ITEM into the given SDB's WORK_QUEUE table. This does
1374    not perform its work within a transaction, assuming the caller will
1375    manage that.  */
1376 static svn_error_t *
1377 add_single_work_item(svn_sqlite__db_t *sdb,
1378                      const svn_skel_t *work_item,
1379                      apr_pool_t *scratch_pool)
1380 {
1381   svn_stringbuf_t *serialized;
1382   svn_sqlite__stmt_t *stmt;
1383
1384   serialized = svn_skel__unparse(work_item, scratch_pool);
1385   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_WORK_ITEM));
1386   SVN_ERR(svn_sqlite__bind_blob(stmt, 1, serialized->data, serialized->len));
1387   return svn_error_trace(svn_sqlite__insert(NULL, stmt));
1388 }
1389
1390
1391 /* Add work item(s) to the given SDB. Also see add_single_work_item(). This
1392    SKEL is usually passed to the various wc_db operation functions. It may
1393    be NULL, indicating no additional work items are needed, it may be a
1394    single work item, or it may be a list of work items.  */
1395 static svn_error_t *
1396 add_work_items(svn_sqlite__db_t *sdb,
1397                const svn_skel_t *skel,
1398                apr_pool_t *scratch_pool)
1399 {
1400   apr_pool_t *iterpool;
1401
1402   /* Maybe there are no work items to insert.  */
1403   if (skel == NULL)
1404     return SVN_NO_ERROR;
1405
1406   /* Should have a list.  */
1407   SVN_ERR_ASSERT(!skel->is_atom);
1408
1409   /* Is the list a single work item? Or a list of work items?  */
1410   if (SVN_WC__SINGLE_WORK_ITEM(skel))
1411     return svn_error_trace(add_single_work_item(sdb, skel, scratch_pool));
1412
1413   /* SKEL is a list-of-lists, aka list of work items.  */
1414
1415   iterpool = svn_pool_create(scratch_pool);
1416   for (skel = skel->children; skel; skel = skel->next)
1417     {
1418       svn_pool_clear(iterpool);
1419
1420       SVN_ERR(add_single_work_item(sdb, skel, iterpool));
1421     }
1422   svn_pool_destroy(iterpool);
1423
1424   return SVN_NO_ERROR;
1425 }
1426
1427
1428 /* Determine whether the node exists for a given WCROOT and LOCAL_RELPATH.  */
1429 static svn_error_t *
1430 does_node_exist(svn_boolean_t *exists,
1431                 const svn_wc__db_wcroot_t *wcroot,
1432                 const char *local_relpath)
1433 {
1434   svn_sqlite__stmt_t *stmt;
1435
1436   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DOES_NODE_EXIST));
1437   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1438   SVN_ERR(svn_sqlite__step(exists, stmt));
1439
1440   return svn_error_trace(svn_sqlite__reset(stmt));
1441 }
1442
1443 svn_error_t *
1444 svn_wc__db_install_schema_statistics(svn_sqlite__db_t *sdb,
1445                                      apr_pool_t *scratch_pool)
1446 {
1447   SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_INSTALL_SCHEMA_STATISTICS));
1448
1449   return SVN_NO_ERROR;
1450 }
1451
1452 /* Helper for create_db(). Initializes our wc.db schema.
1453  */
1454 static svn_error_t *
1455 init_db(/* output values */
1456         apr_int64_t *repos_id,
1457         apr_int64_t *wc_id,
1458         /* input values */
1459         svn_sqlite__db_t *db,
1460         const char *repos_root_url,
1461         const char *repos_uuid,
1462         const char *root_node_repos_relpath,
1463         svn_revnum_t root_node_revision,
1464         svn_depth_t root_node_depth,
1465         apr_pool_t *scratch_pool)
1466 {
1467   svn_sqlite__stmt_t *stmt;
1468
1469   /* Create the database's schema.  */
1470   SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_SCHEMA));
1471   SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES));
1472   SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_NODES_TRIGGERS));
1473   SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_EXTERNALS));
1474
1475   /* Insert the repository. */
1476   SVN_ERR(create_repos_id(repos_id, repos_root_url, repos_uuid,
1477                           db, scratch_pool));
1478
1479   SVN_ERR(svn_wc__db_install_schema_statistics(db, scratch_pool));
1480
1481   /* Insert the wcroot. */
1482   /* ### Right now, this just assumes wc metadata is being stored locally. */
1483   SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT));
1484   SVN_ERR(svn_sqlite__insert(wc_id, stmt));
1485
1486   if (root_node_repos_relpath)
1487     {
1488       svn_wc__db_status_t status = svn_wc__db_status_normal;
1489
1490       if (root_node_revision > 0)
1491         status = svn_wc__db_status_incomplete; /* Will be filled by update */
1492
1493       SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_NODE));
1494       SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtst",
1495                                 *wc_id,              /* 1 */
1496                                 "",                  /* 2 */
1497                                 0,                   /* op_depth is 0 for base */
1498                                 NULL,                /* 4 */
1499                                 *repos_id,
1500                                 root_node_repos_relpath,
1501                                 root_node_revision,
1502                                 presence_map, status, /* 8 */
1503                                 svn_token__to_word(depth_map,
1504                                                    root_node_depth),
1505                                 kind_map, svn_node_dir /* 10 */));
1506
1507       SVN_ERR(svn_sqlite__insert(NULL, stmt));
1508     }
1509
1510   return SVN_NO_ERROR;
1511 }
1512
1513 /* Create an sqlite database at DIR_ABSPATH/SDB_FNAME and insert
1514    records for REPOS_ID (using REPOS_ROOT_URL and REPOS_UUID) into
1515    REPOSITORY and for WC_ID into WCROOT.  Return the DB connection
1516    in *SDB.
1517
1518    If ROOT_NODE_REPOS_RELPATH is not NULL, insert a BASE node at
1519    the working copy root with repository relpath ROOT_NODE_REPOS_RELPATH,
1520    revision ROOT_NODE_REVISION and depth ROOT_NODE_DEPTH.
1521    */
1522 static svn_error_t *
1523 create_db(svn_sqlite__db_t **sdb,
1524           apr_int64_t *repos_id,
1525           apr_int64_t *wc_id,
1526           const char *dir_abspath,
1527           const char *repos_root_url,
1528           const char *repos_uuid,
1529           const char *sdb_fname,
1530           const char *root_node_repos_relpath,
1531           svn_revnum_t root_node_revision,
1532           svn_depth_t root_node_depth,
1533           svn_boolean_t exclusive,
1534           apr_pool_t *result_pool,
1535           apr_pool_t *scratch_pool)
1536 {
1537   SVN_ERR(svn_wc__db_util_open_db(sdb, dir_abspath, sdb_fname,
1538                                   svn_sqlite__mode_rwcreate, exclusive,
1539                                   NULL /* my_statements */,
1540                                   result_pool, scratch_pool));
1541
1542   SVN_SQLITE__WITH_LOCK(init_db(repos_id, wc_id,
1543                                 *sdb, repos_root_url, repos_uuid,
1544                                 root_node_repos_relpath, root_node_revision,
1545                                 root_node_depth, scratch_pool),
1546                         *sdb);
1547
1548   return SVN_NO_ERROR;
1549 }
1550
1551
1552 svn_error_t *
1553 svn_wc__db_init(svn_wc__db_t *db,
1554                 const char *local_abspath,
1555                 const char *repos_relpath,
1556                 const char *repos_root_url,
1557                 const char *repos_uuid,
1558                 svn_revnum_t initial_rev,
1559                 svn_depth_t depth,
1560                 apr_pool_t *scratch_pool)
1561 {
1562   svn_sqlite__db_t *sdb;
1563   apr_int64_t repos_id;
1564   apr_int64_t wc_id;
1565   svn_wc__db_wcroot_t *wcroot;
1566   svn_boolean_t sqlite_exclusive = FALSE;
1567
1568   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1569   SVN_ERR_ASSERT(repos_relpath != NULL);
1570   SVN_ERR_ASSERT(depth == svn_depth_empty
1571                  || depth == svn_depth_files
1572                  || depth == svn_depth_immediates
1573                  || depth == svn_depth_infinity);
1574
1575   /* ### REPOS_ROOT_URL and REPOS_UUID may be NULL. ... more doc: tbd  */
1576
1577   SVN_ERR(svn_config_get_bool((svn_config_t *)db->config, &sqlite_exclusive,
1578                               SVN_CONFIG_SECTION_WORKING_COPY,
1579                               SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
1580                               FALSE));
1581
1582   /* Create the SDB and insert the basic rows.  */
1583   SVN_ERR(create_db(&sdb, &repos_id, &wc_id, local_abspath, repos_root_url,
1584                     repos_uuid, SDB_FILE,
1585                     repos_relpath, initial_rev, depth, sqlite_exclusive,
1586                     db->state_pool, scratch_pool));
1587
1588   /* Create the WCROOT for this directory.  */
1589   SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
1590                         apr_pstrdup(db->state_pool, local_abspath),
1591                         sdb, wc_id, FORMAT_FROM_SDB,
1592                         FALSE /* auto-upgrade */,
1593                         FALSE /* enforce_empty_wq */,
1594                         db->state_pool, scratch_pool));
1595
1596   /* The WCROOT is complete. Stash it into DB.  */
1597   svn_hash_sets(db->dir_data, wcroot->abspath, wcroot);
1598
1599   return SVN_NO_ERROR;
1600 }
1601
1602
1603 svn_error_t *
1604 svn_wc__db_to_relpath(const char **local_relpath,
1605                       svn_wc__db_t *db,
1606                       const char *wri_abspath,
1607                       const char *local_abspath,
1608                       apr_pool_t *result_pool,
1609                       apr_pool_t *scratch_pool)
1610 {
1611   svn_wc__db_wcroot_t *wcroot;
1612   const char *relpath;
1613
1614   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1615
1616   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &relpath, db,
1617                               wri_abspath, result_pool, scratch_pool));
1618
1619   /* This function is indirectly called from the upgrade code, so we
1620      can't verify the wcroot here. Just check that it is not NULL */
1621   CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1622
1623   if (svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
1624     {
1625       *local_relpath = apr_pstrdup(result_pool,
1626                                    svn_dirent_skip_ancestor(wcroot->abspath,
1627                                                             local_abspath));
1628     }
1629   else
1630     /* Probably moving from $TMP. Should we allow this? */
1631     *local_relpath = apr_pstrdup(result_pool, local_abspath);
1632
1633   return SVN_NO_ERROR;
1634 }
1635
1636
1637 svn_error_t *
1638 svn_wc__db_from_relpath(const char **local_abspath,
1639                         svn_wc__db_t *db,
1640                         const char *wri_abspath,
1641                         const char *local_relpath,
1642                         apr_pool_t *result_pool,
1643                         apr_pool_t *scratch_pool)
1644 {
1645   svn_wc__db_wcroot_t *wcroot;
1646   const char *unused_relpath;
1647 #if 0
1648   SVN_ERR_ASSERT(svn_relpath_is_canonical(local_relpath));
1649 #endif
1650
1651   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1652                               wri_abspath, scratch_pool, scratch_pool));
1653
1654   /* This function is indirectly called from the upgrade code, so we
1655      can't verify the wcroot here. Just check that it is not NULL */
1656   CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1657
1658
1659   *local_abspath = svn_dirent_join(wcroot->abspath,
1660                                    local_relpath,
1661                                    result_pool);
1662   return SVN_NO_ERROR;
1663 }
1664
1665
1666 svn_error_t *
1667 svn_wc__db_get_wcroot(const char **wcroot_abspath,
1668                       svn_wc__db_t *db,
1669                       const char *wri_abspath,
1670                       apr_pool_t *result_pool,
1671                       apr_pool_t *scratch_pool)
1672 {
1673   svn_wc__db_wcroot_t *wcroot;
1674   const char *unused_relpath;
1675
1676   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1677                               wri_abspath, scratch_pool, scratch_pool));
1678
1679   /* Can't use VERIFY_USABLE_WCROOT, as this should be usable to detect
1680      where call upgrade */
1681   CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1682
1683   *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
1684
1685   return SVN_NO_ERROR;
1686 }
1687
1688
1689 svn_error_t *
1690 svn_wc__db_base_add_directory(svn_wc__db_t *db,
1691                               const char *local_abspath,
1692                               const char *wri_abspath,
1693                               const char *repos_relpath,
1694                               const char *repos_root_url,
1695                               const char *repos_uuid,
1696                               svn_revnum_t revision,
1697                               const apr_hash_t *props,
1698                               svn_revnum_t changed_rev,
1699                               apr_time_t changed_date,
1700                               const char *changed_author,
1701                               const apr_array_header_t *children,
1702                               svn_depth_t depth,
1703                               apr_hash_t *dav_cache,
1704                               const svn_skel_t *conflict,
1705                               svn_boolean_t update_actual_props,
1706                               apr_hash_t *new_actual_props,
1707                               apr_array_header_t *new_iprops,
1708                               const svn_skel_t *work_items,
1709                               apr_pool_t *scratch_pool)
1710 {
1711   svn_wc__db_wcroot_t *wcroot;
1712   const char *local_relpath;
1713   insert_base_baton_t ibb;
1714
1715   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1716   SVN_ERR_ASSERT(repos_relpath != NULL);
1717   SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1718   SVN_ERR_ASSERT(repos_uuid != NULL);
1719   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1720   SVN_ERR_ASSERT(props != NULL);
1721   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1722 #if 0
1723   SVN_ERR_ASSERT(children != NULL);
1724 #endif
1725
1726   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1727                               wri_abspath, scratch_pool, scratch_pool));
1728   VERIFY_USABLE_WCROOT(wcroot);
1729   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1730
1731   blank_ibb(&ibb);
1732
1733   /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1734   ibb.repos_root_url = repos_root_url;
1735   ibb.repos_uuid = repos_uuid;
1736
1737   ibb.status = svn_wc__db_status_normal;
1738   ibb.kind = svn_node_dir;
1739   ibb.repos_relpath = repos_relpath;
1740   ibb.revision = revision;
1741
1742   ibb.iprops = new_iprops;
1743   ibb.props = props;
1744   ibb.changed_rev = changed_rev;
1745   ibb.changed_date = changed_date;
1746   ibb.changed_author = changed_author;
1747
1748   ibb.children = children;
1749   ibb.depth = depth;
1750
1751   ibb.dav_cache = dav_cache;
1752   ibb.conflict = conflict;
1753   ibb.work_items = work_items;
1754
1755   if (update_actual_props)
1756     {
1757       ibb.update_actual_props = TRUE;
1758       ibb.new_actual_props = new_actual_props;
1759     }
1760
1761   /* Insert the directory and all its children transactionally.
1762
1763      Note: old children can stick around, even if they are no longer present
1764      in this directory's revision.  */
1765   SVN_WC__DB_WITH_TXN(
1766             insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1767             wcroot);
1768
1769   SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
1770   return SVN_NO_ERROR;
1771 }
1772
1773 svn_error_t *
1774 svn_wc__db_base_add_incomplete_directory(svn_wc__db_t *db,
1775                                          const char *local_abspath,
1776                                          const char *repos_relpath,
1777                                          const char *repos_root_url,
1778                                          const char *repos_uuid,
1779                                          svn_revnum_t revision,
1780                                          svn_depth_t depth,
1781                                          svn_boolean_t insert_base_deleted,
1782                                          svn_boolean_t delete_working,
1783                                          svn_skel_t *conflict,
1784                                          svn_skel_t *work_items,
1785                                          apr_pool_t *scratch_pool)
1786 {
1787   svn_wc__db_wcroot_t *wcroot;
1788   const char *local_relpath;
1789   struct insert_base_baton_t ibb;
1790
1791   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1792   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1793   SVN_ERR_ASSERT(repos_relpath && repos_root_url && repos_uuid);
1794
1795   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
1796                                                 db, local_abspath,
1797                                                 scratch_pool, scratch_pool));
1798
1799   VERIFY_USABLE_WCROOT(wcroot);
1800
1801   blank_ibb(&ibb);
1802
1803   /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1804   ibb.repos_root_url = repos_root_url;
1805   ibb.repos_uuid = repos_uuid;
1806
1807   ibb.status = svn_wc__db_status_incomplete;
1808   ibb.kind = svn_node_dir;
1809   ibb.repos_relpath = repos_relpath;
1810   ibb.revision = revision;
1811   ibb.depth = depth;
1812   ibb.insert_base_deleted = insert_base_deleted;
1813   ibb.delete_working = delete_working;
1814
1815   ibb.conflict = conflict;
1816   ibb.work_items = work_items;
1817
1818   SVN_WC__DB_WITH_TXN(
1819             insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1820             wcroot);
1821
1822   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
1823
1824   return SVN_NO_ERROR;
1825 }
1826
1827
1828 svn_error_t *
1829 svn_wc__db_base_add_file(svn_wc__db_t *db,
1830                          const char *local_abspath,
1831                          const char *wri_abspath,
1832                          const char *repos_relpath,
1833                          const char *repos_root_url,
1834                          const char *repos_uuid,
1835                          svn_revnum_t revision,
1836                          const apr_hash_t *props,
1837                          svn_revnum_t changed_rev,
1838                          apr_time_t changed_date,
1839                          const char *changed_author,
1840                          const svn_checksum_t *checksum,
1841                          apr_hash_t *dav_cache,
1842                          svn_boolean_t delete_working,
1843                          svn_boolean_t update_actual_props,
1844                          apr_hash_t *new_actual_props,
1845                          apr_array_header_t *new_iprops,
1846                          svn_boolean_t keep_recorded_info,
1847                          svn_boolean_t insert_base_deleted,
1848                          const svn_skel_t *conflict,
1849                          const svn_skel_t *work_items,
1850                          apr_pool_t *scratch_pool)
1851 {
1852   svn_wc__db_wcroot_t *wcroot;
1853   const char *local_relpath;
1854   insert_base_baton_t ibb;
1855
1856   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1857   SVN_ERR_ASSERT(repos_relpath != NULL);
1858   SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1859   SVN_ERR_ASSERT(repos_uuid != NULL);
1860   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1861   SVN_ERR_ASSERT(props != NULL);
1862   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1863   SVN_ERR_ASSERT(checksum != NULL);
1864
1865   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1866                               wri_abspath, scratch_pool, scratch_pool));
1867   VERIFY_USABLE_WCROOT(wcroot);
1868   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1869
1870   blank_ibb(&ibb);
1871
1872   /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1873   ibb.repos_root_url = repos_root_url;
1874   ibb.repos_uuid = repos_uuid;
1875
1876   ibb.status = svn_wc__db_status_normal;
1877   ibb.kind = svn_node_file;
1878   ibb.repos_relpath = repos_relpath;
1879   ibb.revision = revision;
1880
1881   ibb.props = props;
1882   ibb.changed_rev = changed_rev;
1883   ibb.changed_date = changed_date;
1884   ibb.changed_author = changed_author;
1885
1886   ibb.checksum = checksum;
1887
1888   ibb.dav_cache = dav_cache;
1889   ibb.iprops = new_iprops;
1890
1891   if (update_actual_props)
1892     {
1893       ibb.update_actual_props = TRUE;
1894       ibb.new_actual_props = new_actual_props;
1895     }
1896
1897   ibb.keep_recorded_info = keep_recorded_info;
1898   ibb.insert_base_deleted = insert_base_deleted;
1899   ibb.delete_working = delete_working;
1900
1901   ibb.conflict = conflict;
1902   ibb.work_items = work_items;
1903
1904   SVN_WC__DB_WITH_TXN(
1905             insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1906             wcroot);
1907
1908   /* If this used to be a directory we should remove children so pass
1909    * depth infinity. */
1910   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1911                         scratch_pool));
1912   return SVN_NO_ERROR;
1913 }
1914
1915
1916 svn_error_t *
1917 svn_wc__db_base_add_symlink(svn_wc__db_t *db,
1918                             const char *local_abspath,
1919                             const char *wri_abspath,
1920                             const char *repos_relpath,
1921                             const char *repos_root_url,
1922                             const char *repos_uuid,
1923                             svn_revnum_t revision,
1924                             const apr_hash_t *props,
1925                             svn_revnum_t changed_rev,
1926                             apr_time_t changed_date,
1927                             const char *changed_author,
1928                             const char *target,
1929                             apr_hash_t *dav_cache,
1930                             svn_boolean_t delete_working,
1931                             svn_boolean_t update_actual_props,
1932                             apr_hash_t *new_actual_props,
1933                             apr_array_header_t *new_iprops,
1934                             svn_boolean_t keep_recorded_info,
1935                             svn_boolean_t insert_base_deleted,
1936                             const svn_skel_t *conflict,
1937                             const svn_skel_t *work_items,
1938                             apr_pool_t *scratch_pool)
1939 {
1940   svn_wc__db_wcroot_t *wcroot;
1941   const char *local_relpath;
1942   insert_base_baton_t ibb;
1943
1944   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1945   SVN_ERR_ASSERT(repos_relpath != NULL);
1946   SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1947   SVN_ERR_ASSERT(repos_uuid != NULL);
1948   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1949   SVN_ERR_ASSERT(props != NULL);
1950   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1951   SVN_ERR_ASSERT(target != NULL);
1952
1953   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1954                               wri_abspath, scratch_pool, scratch_pool));
1955   VERIFY_USABLE_WCROOT(wcroot);
1956   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1957   blank_ibb(&ibb);
1958
1959   /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1960   ibb.repos_root_url = repos_root_url;
1961   ibb.repos_uuid = repos_uuid;
1962
1963   ibb.status = svn_wc__db_status_normal;
1964   ibb.kind = svn_node_symlink;
1965   ibb.repos_relpath = repos_relpath;
1966   ibb.revision = revision;
1967
1968   ibb.props = props;
1969   ibb.changed_rev = changed_rev;
1970   ibb.changed_date = changed_date;
1971   ibb.changed_author = changed_author;
1972
1973   ibb.target = target;
1974
1975   ibb.dav_cache = dav_cache;
1976   ibb.iprops = new_iprops;
1977
1978   if (update_actual_props)
1979     {
1980       ibb.update_actual_props = TRUE;
1981       ibb.new_actual_props = new_actual_props;
1982     }
1983
1984   ibb.keep_recorded_info = keep_recorded_info;
1985   ibb.insert_base_deleted = insert_base_deleted;
1986   ibb.delete_working = delete_working;
1987
1988   ibb.conflict = conflict;
1989   ibb.work_items = work_items;
1990
1991   SVN_WC__DB_WITH_TXN(
1992             insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1993             wcroot);
1994
1995   /* If this used to be a directory we should remove children so pass
1996    * depth infinity. */
1997   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1998                         scratch_pool));
1999   return SVN_NO_ERROR;
2000 }
2001
2002
2003 static svn_error_t *
2004 add_excluded_or_not_present_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_wc__db_wcroot_t *wcroot;
2017   const char *local_relpath;
2018   insert_base_baton_t ibb;
2019   const char *dir_abspath, *name;
2020
2021   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2022   SVN_ERR_ASSERT(repos_relpath != NULL);
2023   SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
2024   SVN_ERR_ASSERT(repos_uuid != NULL);
2025   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
2026   SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
2027                  || status == svn_wc__db_status_excluded
2028                  || status == svn_wc__db_status_not_present);
2029
2030   /* These absent presence nodes are only useful below a parent node that is
2031      present. To avoid problems with working copies obstructing the child
2032      we calculate the wcroot and local_relpath of the parent and then add
2033      our own relpath. */
2034
2035   svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
2036
2037   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2038                               dir_abspath, scratch_pool, scratch_pool));
2039   VERIFY_USABLE_WCROOT(wcroot);
2040
2041   local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
2042
2043   blank_ibb(&ibb);
2044
2045   /* Calculate repos_id in insert_base_node() to avoid extra transaction */
2046   ibb.repos_root_url = repos_root_url;
2047   ibb.repos_uuid = repos_uuid;
2048
2049   ibb.status = status;
2050   ibb.kind = kind;
2051   ibb.repos_relpath = repos_relpath;
2052   ibb.revision = revision;
2053
2054   /* Depending upon KIND, any of these might get used. */
2055   ibb.children = NULL;
2056   ibb.depth = svn_depth_unknown;
2057   ibb.checksum = NULL;
2058   ibb.target = NULL;
2059
2060   ibb.conflict = conflict;
2061   ibb.work_items = work_items;
2062
2063   SVN_WC__DB_WITH_TXN(
2064             insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
2065             wcroot);
2066
2067   /* If this used to be a directory we should remove children so pass
2068    * depth infinity. */
2069   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2070                         scratch_pool));
2071
2072   return SVN_NO_ERROR;
2073 }
2074
2075
2076 svn_error_t *
2077 svn_wc__db_base_add_excluded_node(svn_wc__db_t *db,
2078                                   const char *local_abspath,
2079                                   const char *repos_relpath,
2080                                   const char *repos_root_url,
2081                                   const char *repos_uuid,
2082                                   svn_revnum_t revision,
2083                                   svn_node_kind_t kind,
2084                                   svn_wc__db_status_t status,
2085                                   const svn_skel_t *conflict,
2086                                   const svn_skel_t *work_items,
2087                                   apr_pool_t *scratch_pool)
2088 {
2089   SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
2090                  || status == svn_wc__db_status_excluded);
2091
2092   return add_excluded_or_not_present_node(
2093     db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2094     kind, status, conflict, work_items, scratch_pool);
2095 }
2096
2097
2098 svn_error_t *
2099 svn_wc__db_base_add_not_present_node(svn_wc__db_t *db,
2100                                      const char *local_abspath,
2101                                      const char *repos_relpath,
2102                                      const char *repos_root_url,
2103                                      const char *repos_uuid,
2104                                      svn_revnum_t revision,
2105                                      svn_node_kind_t kind,
2106                                      const svn_skel_t *conflict,
2107                                      const svn_skel_t *work_items,
2108                                      apr_pool_t *scratch_pool)
2109 {
2110   return add_excluded_or_not_present_node(
2111     db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2112     kind, svn_wc__db_status_not_present, conflict, work_items, scratch_pool);
2113 }
2114
2115 /* Recursively clear moved-here information at the copy-half of the move
2116  * which moved the node at SRC_RELPATH away. This transforms the move into
2117  * a simple copy. */
2118 static svn_error_t *
2119 clear_moved_here(const char *src_relpath,
2120                  svn_wc__db_wcroot_t *wcroot,
2121                  apr_pool_t *scratch_pool)
2122 {
2123   svn_sqlite__stmt_t *stmt;
2124   const char *dst_relpath;
2125
2126   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
2127   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2128                             src_relpath, relpath_depth(src_relpath)));
2129   SVN_ERR(svn_sqlite__step_row(stmt));
2130   dst_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
2131   SVN_ERR(svn_sqlite__reset(stmt));
2132
2133   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2134                                     STMT_CLEAR_MOVED_HERE_RECURSIVE));
2135   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2136                             dst_relpath, relpath_depth(dst_relpath)));
2137   SVN_ERR(svn_sqlite__step_done(stmt));
2138
2139   return SVN_NO_ERROR;
2140 }
2141
2142 /* The body of svn_wc__db_base_remove().
2143  */
2144 static svn_error_t *
2145 db_base_remove(svn_wc__db_wcroot_t *wcroot,
2146                const char *local_relpath,
2147                svn_wc__db_t *db, /* For checking conflicts */
2148                svn_boolean_t keep_as_working,
2149                svn_boolean_t queue_deletes,
2150                svn_boolean_t remove_locks,
2151                svn_revnum_t not_present_revision,
2152                svn_skel_t *conflict,
2153                svn_skel_t *work_items,
2154                apr_pool_t *scratch_pool)
2155 {
2156   svn_sqlite__stmt_t *stmt;
2157   svn_boolean_t have_row;
2158   svn_wc__db_status_t status;
2159   apr_int64_t repos_id;
2160   const char *repos_relpath;
2161   svn_node_kind_t kind;
2162   svn_boolean_t keep_working;
2163
2164   SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, NULL,
2165                                             &repos_relpath, &repos_id,
2166                                             NULL, NULL, NULL, NULL, NULL,
2167                                             NULL, NULL, NULL, NULL, NULL,
2168                                             wcroot, local_relpath,
2169                                             scratch_pool, scratch_pool));
2170
2171   if (remove_locks)
2172     {
2173       svn_sqlite__stmt_t *lock_stmt;
2174
2175       SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
2176                                         STMT_DELETE_LOCK_RECURSIVELY));
2177       SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
2178       SVN_ERR(svn_sqlite__step_done(lock_stmt));
2179     }
2180
2181   if (status == svn_wc__db_status_normal
2182       && keep_as_working)
2183     {
2184       SVN_ERR(svn_wc__db_op_make_copy(db,
2185                                       svn_dirent_join(wcroot->abspath,
2186                                                       local_relpath,
2187                                                       scratch_pool),
2188                                       NULL, NULL,
2189                                       scratch_pool));
2190       keep_working = TRUE;
2191     }
2192   else
2193     {
2194       /* Check if there is already a working node */
2195       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2196                                         STMT_SELECT_WORKING_NODE));
2197       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2198       SVN_ERR(svn_sqlite__step(&keep_working, stmt));
2199       SVN_ERR(svn_sqlite__reset(stmt));
2200     }
2201
2202   /* Step 1: Create workqueue operations to remove files and dirs in the
2203      local-wc */
2204   if (!keep_working
2205       && queue_deletes
2206       && (status == svn_wc__db_status_normal
2207           || status == svn_wc__db_status_incomplete))
2208     {
2209       svn_skel_t *work_item;
2210       const char *local_abspath;
2211
2212       local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
2213                                       scratch_pool);
2214       if (kind == svn_node_dir)
2215         {
2216           apr_pool_t *iterpool;
2217           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2218                                             STMT_SELECT_BASE_PRESENT));
2219           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2220
2221           iterpool = svn_pool_create(scratch_pool);
2222
2223           SVN_ERR(svn_sqlite__step(&have_row, stmt));
2224
2225           while (have_row)
2226             {
2227               const char *node_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2228               svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 1,
2229                                                               kind_map);
2230               const char *node_abspath;
2231               svn_error_t *err;
2232
2233               svn_pool_clear(iterpool);
2234
2235               node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
2236                                              iterpool);
2237
2238               if (node_kind == svn_node_dir)
2239                 err = svn_wc__wq_build_dir_remove(&work_item,
2240                                                   db, wcroot->abspath,
2241                                                   node_abspath, FALSE,
2242                                                   iterpool, iterpool);
2243               else
2244                 err = svn_wc__wq_build_file_remove(&work_item,
2245                                                    db,
2246                                                    wcroot->abspath,
2247                                                    node_abspath,
2248                                                    iterpool, iterpool);
2249
2250               if (!err)
2251                 err = add_work_items(wcroot->sdb, work_item, iterpool);
2252               if (err)
2253                 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2254
2255               SVN_ERR(svn_sqlite__step(&have_row, stmt));
2256            }
2257
2258           SVN_ERR(svn_sqlite__reset(stmt));
2259
2260           SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
2261                                               db, wcroot->abspath,
2262                                               local_abspath, FALSE,
2263                                               scratch_pool, iterpool));
2264           svn_pool_destroy(iterpool);
2265         }
2266       else
2267         SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
2268                                              db, wcroot->abspath,
2269                                              local_abspath,
2270                                              scratch_pool, scratch_pool));
2271
2272       SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
2273     }
2274
2275   /* Step 2: Delete ACTUAL nodes */
2276   if (! keep_working)
2277     {
2278       /* There won't be a record in NODE left for this node, so we want
2279          to remove *all* ACTUAL nodes, including ACTUAL ONLY. */
2280       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2281                                         STMT_DELETE_ACTUAL_NODE_RECURSIVE));
2282       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2283       SVN_ERR(svn_sqlite__step_done(stmt));
2284     }
2285   else if (! keep_as_working)
2286     {
2287       /* Delete only the ACTUAL nodes that apply to a delete of a BASE node */
2288       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2289                                        STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE));
2290       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2291       SVN_ERR(svn_sqlite__step_done(stmt));
2292     }
2293   /* Else: Everything has been turned into a copy, so we want to keep all
2294            ACTUAL_NODE records */
2295
2296   /* Step 3: Delete WORKING nodes */
2297   if (conflict)
2298     {
2299       apr_pool_t *iterpool;
2300
2301       /*
2302        * When deleting a conflicted node, moves of any moved-outside children
2303        * of the node must be broken. Else, the destination will still be marked
2304        * moved-here after the move source disappears from the working copy.
2305        *
2306        * ### FIXME: It would be nicer to have the conflict resolver
2307        * break the move instead. It might also be a good idea to
2308        * flag a tree conflict on each moved-away child. But doing so
2309        * might introduce actual-only nodes without direct parents,
2310        * and we're not yet sure if other existing code is prepared
2311        * to handle such nodes. To be revisited post-1.8.
2312        *
2313        * ### In case of a conflict we are most likely creating WORKING nodes
2314        *     describing a copy of what was in BASE. The move information
2315        *     should be updated to describe a move from the WORKING layer.
2316        *     When stored that way the resolver of the tree conflict still has
2317        *     the knowledge of what was moved.
2318        */
2319       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2320                                         STMT_SELECT_MOVED_OUTSIDE));
2321       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2322                                              local_relpath,
2323                                              relpath_depth(local_relpath)));
2324       SVN_ERR(svn_sqlite__step(&have_row, stmt));
2325       iterpool = svn_pool_create(scratch_pool);
2326       while (have_row)
2327         {
2328           const char *child_relpath;
2329           svn_error_t *err;
2330
2331           svn_pool_clear(iterpool);
2332           child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
2333           err = clear_moved_here(child_relpath, wcroot, iterpool);
2334           if (err)
2335             return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2336           SVN_ERR(svn_sqlite__step(&have_row, stmt));
2337         }
2338       svn_pool_destroy(iterpool);
2339       SVN_ERR(svn_sqlite__reset(stmt));
2340     }
2341   if (keep_working)
2342     {
2343       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2344                                         STMT_DELETE_WORKING_BASE_DELETE));
2345       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
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   /* Step 5: handle the BASE node itself */
2363   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2364                                     STMT_DELETE_BASE_NODE));
2365   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2366   SVN_ERR(svn_sqlite__step_done(stmt));
2367
2368   SVN_ERR(svn_wc__db_retract_parent_delete(wcroot, local_relpath, 0,
2369                                            scratch_pool));
2370
2371   /* Step 6: Delete actual node if we don't keep working */
2372   if (! keep_working)
2373     {
2374       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2375                                         STMT_DELETE_ACTUAL_NODE));
2376       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2377       SVN_ERR(svn_sqlite__step_done(stmt));
2378     }
2379
2380   if (SVN_IS_VALID_REVNUM(not_present_revision))
2381     {
2382       struct insert_base_baton_t ibb;
2383       blank_ibb(&ibb);
2384
2385       ibb.repos_id = repos_id;
2386       ibb.status = svn_wc__db_status_not_present;
2387       ibb.kind = kind;
2388       ibb.repos_relpath = repos_relpath;
2389       ibb.revision = not_present_revision;
2390
2391       /* Depending upon KIND, any of these might get used. */
2392       ibb.children = NULL;
2393       ibb.depth = svn_depth_unknown;
2394       ibb.checksum = NULL;
2395       ibb.target = NULL;
2396
2397       SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
2398     }
2399
2400   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2401   if (conflict)
2402     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
2403                                               conflict, scratch_pool));
2404
2405   return SVN_NO_ERROR;
2406 }
2407
2408
2409 svn_error_t *
2410 svn_wc__db_base_remove(svn_wc__db_t *db,
2411                        const char *local_abspath,
2412                        svn_boolean_t keep_as_working,
2413                        svn_boolean_t queue_deletes,
2414                        svn_boolean_t remove_locks,
2415                        svn_revnum_t not_present_revision,
2416                        svn_skel_t *conflict,
2417                        svn_skel_t *work_items,
2418                        apr_pool_t *scratch_pool)
2419 {
2420   svn_wc__db_wcroot_t *wcroot;
2421   const char *local_relpath;
2422
2423   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2424
2425   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2426                               local_abspath, scratch_pool, scratch_pool));
2427   VERIFY_USABLE_WCROOT(wcroot);
2428
2429   SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath,
2430                                      db, keep_as_working, queue_deletes,
2431                                      remove_locks, not_present_revision,
2432                                      conflict, work_items, scratch_pool),
2433                       wcroot);
2434
2435   /* If this used to be a directory we should remove children so pass
2436    * depth infinity. */
2437   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2438                         scratch_pool));
2439
2440   return SVN_NO_ERROR;
2441 }
2442
2443
2444 svn_error_t *
2445 svn_wc__db_base_get_info_internal(svn_wc__db_status_t *status,
2446                                   svn_node_kind_t *kind,
2447                                   svn_revnum_t *revision,
2448                                   const char **repos_relpath,
2449                                   apr_int64_t *repos_id,
2450                                   svn_revnum_t *changed_rev,
2451                                   apr_time_t *changed_date,
2452                                   const char **changed_author,
2453                                   svn_depth_t *depth,
2454                                   const svn_checksum_t **checksum,
2455                                   const char **target,
2456                                   svn_wc__db_lock_t **lock,
2457                                   svn_boolean_t *had_props,
2458                                   apr_hash_t **props,
2459                                   svn_boolean_t *update_root,
2460                                   svn_wc__db_wcroot_t *wcroot,
2461                                   const char *local_relpath,
2462                                   apr_pool_t *result_pool,
2463                                   apr_pool_t *scratch_pool)
2464 {
2465   svn_sqlite__stmt_t *stmt;
2466   svn_boolean_t have_row;
2467   svn_error_t *err = SVN_NO_ERROR;
2468
2469   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2470                                     lock ? STMT_SELECT_BASE_NODE_WITH_LOCK
2471                                          : STMT_SELECT_BASE_NODE));
2472   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2473   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2474
2475   if (have_row)
2476     {
2477       svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2478                                                                  presence_map);
2479       svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2480
2481       if (kind)
2482         {
2483           *kind = node_kind;
2484         }
2485       if (status)
2486         {
2487           *status = node_status;
2488         }
2489       repos_location_from_columns(repos_id, revision, repos_relpath,
2490                                   stmt, 0, 4, 1, result_pool);
2491       SVN_ERR_ASSERT(!repos_id || *repos_id != INVALID_REPOS_ID);
2492       SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
2493       if (lock)
2494         {
2495           *lock = lock_from_columns(stmt, 15, 16, 17, 18, result_pool);
2496         }
2497       if (changed_rev)
2498         {
2499           *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2500         }
2501       if (changed_date)
2502         {
2503           *changed_date = svn_sqlite__column_int64(stmt, 8);
2504         }
2505       if (changed_author)
2506         {
2507           /* Result may be NULL. */
2508           *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2509         }
2510       if (depth)
2511         {
2512           if (node_kind != svn_node_dir)
2513             {
2514               *depth = svn_depth_unknown;
2515             }
2516           else
2517             {
2518               *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2519                                                      svn_depth_unknown);
2520             }
2521         }
2522       if (checksum)
2523         {
2524           if (node_kind != svn_node_file)
2525             {
2526               *checksum = NULL;
2527             }
2528           else
2529             {
2530               err = svn_sqlite__column_checksum(checksum, stmt, 5,
2531                                                 result_pool);
2532               if (err != NULL)
2533                 err = svn_error_createf(
2534                         err->apr_err, err,
2535                         _("The node '%s' has a corrupt checksum value."),
2536                         path_for_error_message(wcroot, local_relpath,
2537                                                scratch_pool));
2538             }
2539         }
2540       if (target)
2541         {
2542           if (node_kind != svn_node_symlink)
2543             *target = NULL;
2544           else
2545             *target = svn_sqlite__column_text(stmt, 11, result_pool);
2546         }
2547       if (had_props)
2548         {
2549           *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2550         }
2551       if (props)
2552         {
2553           if (node_status == svn_wc__db_status_normal
2554               || node_status == svn_wc__db_status_incomplete)
2555             {
2556               SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2557                                                     result_pool, scratch_pool));
2558               if (*props == NULL)
2559                 *props = apr_hash_make(result_pool);
2560             }
2561           else
2562             {
2563               assert(svn_sqlite__column_is_null(stmt, 13));
2564               *props = NULL;
2565             }
2566         }
2567       if (update_root)
2568         {
2569           /* It's an update root iff it's a file external. */
2570           *update_root = svn_sqlite__column_boolean(stmt, 14);
2571         }
2572     }
2573   else
2574     {
2575       err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2576                               _("The node '%s' was not found."),
2577                               path_for_error_message(wcroot, local_relpath,
2578                                                      scratch_pool));
2579     }
2580
2581   /* Note: given the composition, no need to wrap for tracing.  */
2582   return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2583 }
2584
2585
2586 svn_error_t *
2587 svn_wc__db_base_get_info(svn_wc__db_status_t *status,
2588                          svn_node_kind_t *kind,
2589                          svn_revnum_t *revision,
2590                          const char **repos_relpath,
2591                          const char **repos_root_url,
2592                          const char **repos_uuid,
2593                          svn_revnum_t *changed_rev,
2594                          apr_time_t *changed_date,
2595                          const char **changed_author,
2596                          svn_depth_t *depth,
2597                          const svn_checksum_t **checksum,
2598                          const char **target,
2599                          svn_wc__db_lock_t **lock,
2600                          svn_boolean_t *had_props,
2601                          apr_hash_t **props,
2602                          svn_boolean_t *update_root,
2603                          svn_wc__db_t *db,
2604                          const char *local_abspath,
2605                          apr_pool_t *result_pool,
2606                          apr_pool_t *scratch_pool)
2607 {
2608   svn_wc__db_wcroot_t *wcroot;
2609   const char *local_relpath;
2610   apr_int64_t repos_id;
2611
2612   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2613
2614   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2615                               local_abspath, scratch_pool, scratch_pool));
2616   VERIFY_USABLE_WCROOT(wcroot);
2617
2618   SVN_ERR(svn_wc__db_base_get_info_internal(status, kind, revision,
2619                                             repos_relpath, &repos_id,
2620                                             changed_rev, changed_date,
2621                                             changed_author, depth,
2622                                             checksum, target, lock,
2623                                             had_props, props, update_root,
2624                                             wcroot, local_relpath,
2625                                             result_pool, scratch_pool));
2626   SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
2627   SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
2628                                       wcroot->sdb, repos_id, result_pool));
2629
2630   return SVN_NO_ERROR;
2631 }
2632
2633 svn_error_t *
2634 svn_wc__db_base_get_children_info(apr_hash_t **nodes,
2635                                   svn_wc__db_t *db,
2636                                   const char *dir_abspath,
2637                                   apr_pool_t *result_pool,
2638                                   apr_pool_t *scratch_pool)
2639 {
2640   svn_wc__db_wcroot_t *wcroot;
2641   const char *local_relpath;
2642   svn_sqlite__stmt_t *stmt;
2643   svn_boolean_t have_row;
2644
2645   SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
2646
2647   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2648                               dir_abspath, scratch_pool, scratch_pool));
2649   VERIFY_USABLE_WCROOT(wcroot);
2650
2651   *nodes = apr_hash_make(result_pool);
2652
2653   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2654                                     STMT_SELECT_BASE_CHILDREN_INFO));
2655   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2656
2657   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2658
2659   while (have_row)
2660     {
2661       struct svn_wc__db_base_info_t *info;
2662       svn_error_t *err;
2663       apr_int64_t repos_id;
2664       const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2665       const char *name = svn_relpath_basename(child_relpath, result_pool);
2666
2667       info = apr_pcalloc(result_pool, sizeof(*info));
2668
2669       repos_id = svn_sqlite__column_int64(stmt, 1);
2670       info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
2671       info->status = svn_sqlite__column_token(stmt, 3, presence_map);
2672       info->kind = svn_sqlite__column_token(stmt, 4, kind_map);
2673       info->revnum = svn_sqlite__column_revnum(stmt, 5);
2674
2675       info->depth = svn_sqlite__column_token_null(stmt, 6, depth_map,
2676                                                   svn_depth_unknown);
2677
2678       info->update_root = svn_sqlite__column_boolean(stmt, 7);
2679
2680       info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool);
2681
2682       err = svn_wc__db_fetch_repos_info(&info->repos_root_url, NULL,
2683                                         wcroot->sdb, repos_id, result_pool);
2684
2685       if (err)
2686         return svn_error_trace(
2687                  svn_error_compose_create(err,
2688                                           svn_sqlite__reset(stmt)));
2689
2690
2691       svn_hash_sets(*nodes, name, info);
2692
2693       SVN_ERR(svn_sqlite__step(&have_row, stmt));
2694     }
2695
2696   SVN_ERR(svn_sqlite__reset(stmt));
2697
2698   return SVN_NO_ERROR;
2699 }
2700
2701
2702 svn_error_t *
2703 svn_wc__db_base_get_props(apr_hash_t **props,
2704                           svn_wc__db_t *db,
2705                           const char *local_abspath,
2706                           apr_pool_t *result_pool,
2707                           apr_pool_t *scratch_pool)
2708 {
2709   svn_wc__db_status_t presence;
2710
2711   SVN_ERR(svn_wc__db_base_get_info(&presence, NULL, NULL, NULL, NULL,
2712                                    NULL, NULL, NULL, NULL, NULL,
2713                                    NULL, NULL, NULL, NULL, props, NULL,
2714                                    db, local_abspath,
2715                                    result_pool, scratch_pool));
2716   if (presence != svn_wc__db_status_normal
2717       && presence != svn_wc__db_status_incomplete)
2718     {
2719       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2720                                _("The node '%s' has a BASE status that"
2721                                   " has no properties."),
2722                                svn_dirent_local_style(local_abspath,
2723                                                       scratch_pool));
2724     }
2725
2726   return SVN_NO_ERROR;
2727 }
2728
2729
2730 svn_error_t *
2731 svn_wc__db_base_get_children(const apr_array_header_t **children,
2732                              svn_wc__db_t *db,
2733                              const char *local_abspath,
2734                              apr_pool_t *result_pool,
2735                              apr_pool_t *scratch_pool)
2736 {
2737   svn_wc__db_wcroot_t *wcroot;
2738   const char *local_relpath;
2739
2740   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2741
2742   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2743                                              local_abspath,
2744                                              scratch_pool, scratch_pool));
2745   VERIFY_USABLE_WCROOT(wcroot);
2746
2747   return gather_repo_children(children, wcroot, local_relpath, 0,
2748                               result_pool, scratch_pool);
2749 }
2750
2751
2752 svn_error_t *
2753 svn_wc__db_base_set_dav_cache(svn_wc__db_t *db,
2754                               const char *local_abspath,
2755                               const apr_hash_t *props,
2756                               apr_pool_t *scratch_pool)
2757 {
2758   svn_sqlite__stmt_t *stmt;
2759   int affected_rows;
2760
2761   SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
2762                                  STMT_UPDATE_BASE_NODE_DAV_CACHE,
2763                                  scratch_pool));
2764   SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
2765
2766   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2767
2768   if (affected_rows != 1)
2769     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2770                              _("The node '%s' was not found."),
2771                              svn_dirent_local_style(local_abspath,
2772                                                     scratch_pool));
2773
2774   return SVN_NO_ERROR;
2775 }
2776
2777
2778 svn_error_t *
2779 svn_wc__db_base_get_dav_cache(apr_hash_t **props,
2780                               svn_wc__db_t *db,
2781                               const char *local_abspath,
2782                               apr_pool_t *result_pool,
2783                               apr_pool_t *scratch_pool)
2784 {
2785   svn_sqlite__stmt_t *stmt;
2786   svn_boolean_t have_row;
2787
2788   SVN_ERR(get_statement_for_path(&stmt, db, local_abspath,
2789                                  STMT_SELECT_BASE_DAV_CACHE, scratch_pool));
2790   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2791   if (!have_row)
2792     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
2793                              svn_sqlite__reset(stmt),
2794                              _("The node '%s' was not found."),
2795                              svn_dirent_local_style(local_abspath,
2796                                                     scratch_pool));
2797
2798   SVN_ERR(svn_sqlite__column_properties(props, stmt, 0, result_pool,
2799                                         scratch_pool));
2800   return svn_error_trace(svn_sqlite__reset(stmt));
2801 }
2802
2803
2804 svn_error_t *
2805 svn_wc__db_base_clear_dav_cache_recursive(svn_wc__db_t *db,
2806                                           const char *local_abspath,
2807                                           apr_pool_t *scratch_pool)
2808 {
2809   svn_wc__db_wcroot_t *wcroot;
2810   const char *local_relpath;
2811   svn_sqlite__stmt_t *stmt;
2812
2813   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2814                                              db, local_abspath,
2815                                              scratch_pool, scratch_pool));
2816   VERIFY_USABLE_WCROOT(wcroot);
2817
2818   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2819                                     STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE));
2820   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2821
2822   SVN_ERR(svn_sqlite__step_done(stmt));
2823
2824   return SVN_NO_ERROR;
2825 }
2826
2827
2828 svn_error_t *
2829 svn_wc__db_depth_get_info(svn_wc__db_status_t *status,
2830                           svn_node_kind_t *kind,
2831                           svn_revnum_t *revision,
2832                           const char **repos_relpath,
2833                           apr_int64_t *repos_id,
2834                           svn_revnum_t *changed_rev,
2835                           apr_time_t *changed_date,
2836                           const char **changed_author,
2837                           svn_depth_t *depth,
2838                           const svn_checksum_t **checksum,
2839                           const char **target,
2840                           svn_boolean_t *had_props,
2841                           apr_hash_t **props,
2842                           svn_wc__db_wcroot_t *wcroot,
2843                           const char *local_relpath,
2844                           int op_depth,
2845                           apr_pool_t *result_pool,
2846                           apr_pool_t *scratch_pool)
2847 {
2848   svn_sqlite__stmt_t *stmt;
2849   svn_boolean_t have_row;
2850   svn_error_t *err = SVN_NO_ERROR;
2851
2852   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2853                                     STMT_SELECT_DEPTH_NODE));
2854   SVN_ERR(svn_sqlite__bindf(stmt, "isd",
2855                             wcroot->wc_id, local_relpath, op_depth));
2856   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2857
2858   if (have_row)
2859     {
2860       svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2861                                                                  presence_map);
2862       svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2863
2864       if (kind)
2865         {
2866           *kind = node_kind;
2867         }
2868       if (status)
2869         {
2870           *status = node_status;
2871
2872           if (op_depth > 0)
2873             SVN_ERR(convert_to_working_status(status, *status));
2874         }
2875       repos_location_from_columns(repos_id, revision, repos_relpath,
2876                                   stmt, 0, 4, 1, result_pool);
2877
2878       if (changed_rev)
2879         {
2880           *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2881         }
2882       if (changed_date)
2883         {
2884           *changed_date = svn_sqlite__column_int64(stmt, 8);
2885         }
2886       if (changed_author)
2887         {
2888           /* Result may be NULL. */
2889           *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2890         }
2891       if (depth)
2892         {
2893           if (node_kind != svn_node_dir)
2894             {
2895               *depth = svn_depth_unknown;
2896             }
2897           else
2898             {
2899               *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2900                                                      svn_depth_unknown);
2901             }
2902         }
2903       if (checksum)
2904         {
2905           if (node_kind != svn_node_file)
2906             {
2907               *checksum = NULL;
2908             }
2909           else
2910             {
2911               err = svn_sqlite__column_checksum(checksum, stmt, 5,
2912                                                 result_pool);
2913               if (err != NULL)
2914                 err = svn_error_createf(
2915                         err->apr_err, err,
2916                         _("The node '%s' has a corrupt checksum value."),
2917                         path_for_error_message(wcroot, local_relpath,
2918                                                scratch_pool));
2919             }
2920         }
2921       if (target)
2922         {
2923           if (node_kind != svn_node_symlink)
2924             *target = NULL;
2925           else
2926             *target = svn_sqlite__column_text(stmt, 11, result_pool);
2927         }
2928       if (had_props)
2929         {
2930           *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2931         }
2932       if (props)
2933         {
2934           if (node_status == svn_wc__db_status_normal
2935               || node_status == svn_wc__db_status_incomplete)
2936             {
2937               SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2938                                                     result_pool, scratch_pool));
2939               if (*props == NULL)
2940                 *props = apr_hash_make(result_pool);
2941             }
2942           else
2943             {
2944               assert(svn_sqlite__column_is_null(stmt, 13));
2945               *props = NULL;
2946             }
2947         }
2948     }
2949   else
2950     {
2951       err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2952                               _("The node '%s' was not found."),
2953                               path_for_error_message(wcroot, local_relpath,
2954                                                      scratch_pool));
2955     }
2956
2957   /* Note: given the composition, no need to wrap for tracing.  */
2958   return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2959 }
2960
2961
2962 /* Baton for passing args to with_triggers(). */
2963 struct with_triggers_baton_t {
2964   int create_trigger;
2965   int drop_trigger;
2966   svn_wc__db_txn_callback_t cb_func;
2967   void *cb_baton;
2968 };
2969
2970 /* Helper for creating SQLite triggers, running the main transaction
2971    callback, and then dropping the triggers.  It guarantees that the
2972    triggers will not survive the transaction.  This could be used for
2973    any general prefix/postscript statements where the postscript
2974    *must* be executed if the transaction completes.
2975
2976    Implements svn_wc__db_txn_callback_t. */
2977 static svn_error_t *
2978 with_triggers(void *baton,
2979               svn_wc__db_wcroot_t *wcroot,
2980               const char *local_relpath,
2981               apr_pool_t *scratch_pool)
2982 {
2983   struct with_triggers_baton_t *b = baton;
2984   svn_error_t *err1;
2985   svn_error_t *err2;
2986
2987   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, b->create_trigger));
2988
2989   err1 = b->cb_func(b->cb_baton, wcroot, local_relpath, scratch_pool);
2990
2991   err2 = svn_sqlite__exec_statements(wcroot->sdb, b->drop_trigger);
2992
2993   return svn_error_trace(svn_error_compose_create(err1, err2));
2994 }
2995
2996
2997 /* Prototype for the "work callback" used by with_finalization().  */
2998 typedef svn_error_t * (*work_callback_t)(
2999                           void *baton,
3000                           svn_wc__db_wcroot_t *wcroot,
3001                           svn_cancel_func_t cancel_func,
3002                           void *cancel_baton,
3003                           svn_wc_notify_func2_t notify_func,
3004                           void *notify_baton,
3005                           apr_pool_t *scratch_pool);
3006
3007 /* Utility function to provide several features, with a guaranteed
3008    finalization (ie. to drop temporary tables).
3009
3010    1) for WCROOT and LOCAL_RELPATH, run TXN_CB(TXN_BATON) within a
3011       sqlite transaction
3012    2) if (1) is successful and a NOTIFY_FUNC is provided, then run
3013       the "work" step: WORK_CB(WORK_BATON).
3014    3) execute FINALIZE_STMT_IDX no matter what errors may be thrown
3015       from the above two steps.
3016
3017    CANCEL_FUNC, CANCEL_BATON, NOTIFY_FUNC and NOTIFY_BATON are their
3018    typical values. These are passed to the work callback, which typically
3019    provides notification about the work done by TXN_CB.  */
3020 static svn_error_t *
3021 with_finalization(svn_wc__db_wcroot_t *wcroot,
3022                   const char *local_relpath,
3023                   svn_wc__db_txn_callback_t txn_cb,
3024                   void *txn_baton,
3025                   work_callback_t work_cb,
3026                   void *work_baton,
3027                   svn_cancel_func_t cancel_func,
3028                   void *cancel_baton,
3029                   svn_wc_notify_func2_t notify_func,
3030                   void *notify_baton,
3031                   int finalize_stmt_idx,
3032                   apr_pool_t *scratch_pool)
3033 {
3034   svn_error_t *err1;
3035   svn_error_t *err2;
3036
3037   err1 = svn_wc__db_with_txn(wcroot, local_relpath, txn_cb, txn_baton,
3038                              scratch_pool);
3039
3040   if (err1 == NULL && notify_func != NULL)
3041     {
3042       err2 = work_cb(work_baton, wcroot,
3043                      cancel_func, cancel_baton,
3044                      notify_func, notify_baton,
3045                      scratch_pool);
3046       err1 = svn_error_compose_create(err1, err2);
3047     }
3048
3049   err2 = svn_sqlite__exec_statements(wcroot->sdb, finalize_stmt_idx);
3050
3051   return svn_error_trace(svn_error_compose_create(err1, err2));
3052 }
3053
3054
3055 /* Initialize the baton with appropriate "blank" values. This allows the
3056    insertion function to leave certain columns null.  */
3057 static void
3058 blank_ieb(insert_external_baton_t *ieb)
3059 {
3060   memset(ieb, 0, sizeof(*ieb));
3061   ieb->revision = SVN_INVALID_REVNUM;
3062   ieb->changed_rev = SVN_INVALID_REVNUM;
3063   ieb->repos_id = INVALID_REPOS_ID;
3064
3065   ieb->recorded_peg_revision = SVN_INVALID_REVNUM;
3066   ieb->recorded_revision = SVN_INVALID_REVNUM;
3067 }
3068
3069 /* Insert the externals row represented by (insert_external_baton_t *) BATON.
3070  *
3071  * Implements svn_wc__db_txn_callback_t. */
3072 static svn_error_t *
3073 insert_external_node(const insert_external_baton_t *ieb,
3074                      svn_wc__db_wcroot_t *wcroot,
3075                      const char *local_relpath,
3076                      apr_pool_t *scratch_pool)
3077 {
3078   svn_wc__db_status_t status;
3079   svn_error_t *err;
3080   svn_boolean_t update_root;
3081   apr_int64_t repos_id;
3082   svn_sqlite__stmt_t *stmt;
3083
3084   if (ieb->repos_id != INVALID_REPOS_ID)
3085     repos_id = ieb->repos_id;
3086   else
3087     SVN_ERR(create_repos_id(&repos_id, ieb->repos_root_url, ieb->repos_uuid,
3088                             wcroot->sdb, scratch_pool));
3089
3090   /* And there must be no existing BASE node or it must be a file external */
3091   err = svn_wc__db_base_get_info_internal(&status, NULL, NULL, NULL, NULL,
3092                                           NULL, NULL, NULL, NULL, NULL,
3093                                           NULL, NULL, NULL, NULL, &update_root,
3094                                           wcroot, local_relpath,
3095                                           scratch_pool, scratch_pool);
3096   if (err)
3097     {
3098       if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3099         return svn_error_trace(err);
3100
3101       svn_error_clear(err);
3102     }
3103   else if (status == svn_wc__db_status_normal && !update_root)
3104     return svn_error_create(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, NULL);
3105
3106   if (ieb->kind == svn_node_file
3107       || ieb->kind == svn_node_symlink)
3108     {
3109       struct insert_base_baton_t ibb;
3110
3111       blank_ibb(&ibb);
3112
3113       ibb.status          = svn_wc__db_status_normal;
3114       ibb.kind            = ieb->kind;
3115
3116       ibb.repos_id        = repos_id;
3117       ibb.repos_relpath   = ieb->repos_relpath;
3118       ibb.revision        = ieb->revision;
3119
3120       ibb.props           = ieb->props;
3121       ibb.iprops          = ieb->iprops;
3122       ibb.changed_rev     = ieb->changed_rev;
3123       ibb.changed_date    = ieb->changed_date;
3124       ibb.changed_author  = ieb->changed_author;
3125
3126       ibb.dav_cache       = ieb->dav_cache;
3127
3128       ibb.checksum        = ieb->checksum;
3129       ibb.target          = ieb->target;
3130
3131       ibb.conflict        = ieb->conflict;
3132
3133       ibb.update_actual_props = ieb->update_actual_props;
3134       ibb.new_actual_props    = ieb->new_actual_props;
3135
3136       ibb.keep_recorded_info  = ieb->keep_recorded_info;
3137
3138       ibb.work_items      = ieb->work_items;
3139
3140       ibb.file_external = TRUE;
3141
3142       SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
3143     }
3144   else
3145     SVN_ERR(add_work_items(wcroot->sdb, ieb->work_items, scratch_pool));
3146
3147   /* The externals table only support presence normal and excluded */
3148   SVN_ERR_ASSERT(ieb->presence == svn_wc__db_status_normal
3149                  || ieb->presence == svn_wc__db_status_excluded);
3150
3151   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_EXTERNAL));
3152
3153   SVN_ERR(svn_sqlite__bindf(stmt, "issttsis",
3154                             wcroot->wc_id,
3155                             local_relpath,
3156                             svn_relpath_dirname(local_relpath,
3157                                                 scratch_pool),
3158                             presence_map, ieb->presence,
3159                             kind_map, ieb->kind,
3160                             ieb->record_ancestor_relpath,
3161                             repos_id,
3162                             ieb->recorded_repos_relpath));
3163
3164   if (SVN_IS_VALID_REVNUM(ieb->recorded_peg_revision))
3165     SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, ieb->recorded_peg_revision));
3166
3167   if (SVN_IS_VALID_REVNUM(ieb->recorded_revision))
3168     SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, ieb->recorded_revision));
3169
3170   SVN_ERR(svn_sqlite__insert(NULL, stmt));
3171
3172   return SVN_NO_ERROR;
3173 }
3174
3175 svn_error_t *
3176 svn_wc__db_external_add_file(svn_wc__db_t *db,
3177                              const char *local_abspath,
3178                              const char *wri_abspath,
3179
3180                              const char *repos_relpath,
3181                              const char *repos_root_url,
3182                              const char *repos_uuid,
3183                              svn_revnum_t revision,
3184
3185                              const apr_hash_t *props,
3186                              apr_array_header_t *iprops,
3187
3188                              svn_revnum_t changed_rev,
3189                              apr_time_t changed_date,
3190                              const char *changed_author,
3191
3192                              const svn_checksum_t *checksum,
3193
3194                              const apr_hash_t *dav_cache,
3195
3196                              const char *record_ancestor_abspath,
3197                              const char *recorded_repos_relpath,
3198                              svn_revnum_t recorded_peg_revision,
3199                              svn_revnum_t recorded_revision,
3200
3201                              svn_boolean_t update_actual_props,
3202                              apr_hash_t *new_actual_props,
3203
3204                              svn_boolean_t keep_recorded_info,
3205                              const svn_skel_t *conflict,
3206                              const svn_skel_t *work_items,
3207                              apr_pool_t *scratch_pool)
3208 {
3209   svn_wc__db_wcroot_t *wcroot;
3210   const char *local_relpath;
3211   insert_external_baton_t ieb;
3212
3213   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3214
3215   if (! wri_abspath)
3216     wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3217
3218   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3219                               wri_abspath, scratch_pool, scratch_pool));
3220   VERIFY_USABLE_WCROOT(wcroot);
3221
3222   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3223                                         record_ancestor_abspath));
3224
3225   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3226
3227   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3228
3229   blank_ieb(&ieb);
3230
3231   ieb.kind = svn_node_file;
3232   ieb.presence = svn_wc__db_status_normal;
3233
3234   ieb.repos_root_url = repos_root_url;
3235   ieb.repos_uuid = repos_uuid;
3236
3237   ieb.repos_relpath = repos_relpath;
3238   ieb.revision = revision;
3239
3240   ieb.props = props;
3241   ieb.iprops = iprops;
3242
3243   ieb.changed_rev = changed_rev;
3244   ieb.changed_date = changed_date;
3245   ieb.changed_author = changed_author;
3246
3247   ieb.checksum = checksum;
3248
3249   ieb.dav_cache = dav_cache;
3250
3251   ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3252                                                 wcroot->abspath,
3253                                                 record_ancestor_abspath);
3254   ieb.recorded_repos_relpath = recorded_repos_relpath;
3255   ieb.recorded_peg_revision = recorded_peg_revision;
3256   ieb.recorded_revision = recorded_revision;
3257
3258   ieb.update_actual_props = update_actual_props;
3259   ieb.new_actual_props = new_actual_props;
3260
3261   ieb.keep_recorded_info = keep_recorded_info;
3262
3263   ieb.conflict = conflict;
3264   ieb.work_items = work_items;
3265
3266   SVN_WC__DB_WITH_TXN(
3267             insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3268             wcroot);
3269
3270   return SVN_NO_ERROR;
3271 }
3272
3273 svn_error_t *
3274 svn_wc__db_external_add_symlink(svn_wc__db_t *db,
3275                                 const char *local_abspath,
3276                                 const char *wri_abspath,
3277                                 const char *repos_relpath,
3278                                 const char *repos_root_url,
3279                                 const char *repos_uuid,
3280                                 svn_revnum_t revision,
3281                                 const apr_hash_t *props,
3282                                 svn_revnum_t changed_rev,
3283                                 apr_time_t changed_date,
3284                                 const char *changed_author,
3285                                 const char *target,
3286                                 const apr_hash_t *dav_cache,
3287                                 const char *record_ancestor_abspath,
3288                                 const char *recorded_repos_relpath,
3289                                 svn_revnum_t recorded_peg_revision,
3290                                 svn_revnum_t recorded_revision,
3291                                 svn_boolean_t update_actual_props,
3292                                 apr_hash_t *new_actual_props,
3293                                 svn_boolean_t keep_recorded_info,
3294                                 const svn_skel_t *work_items,
3295                                 apr_pool_t *scratch_pool)
3296 {
3297   svn_wc__db_wcroot_t *wcroot;
3298   const char *local_relpath;
3299   insert_external_baton_t ieb;
3300
3301   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3302
3303   if (! wri_abspath)
3304     wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3305
3306   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3307                               wri_abspath, scratch_pool, scratch_pool));
3308   VERIFY_USABLE_WCROOT(wcroot);
3309
3310   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3311                                         record_ancestor_abspath));
3312
3313   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3314
3315   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3316
3317   blank_ieb(&ieb);
3318
3319   ieb.kind = svn_node_symlink;
3320   ieb.presence = svn_wc__db_status_normal;
3321
3322   ieb.repos_root_url = repos_root_url;
3323   ieb.repos_uuid = repos_uuid;
3324
3325   ieb.repos_relpath = repos_relpath;
3326   ieb.revision = revision;
3327
3328   ieb.props = props;
3329
3330   ieb.changed_rev = changed_rev;
3331   ieb.changed_date = changed_date;
3332   ieb.changed_author = changed_author;
3333
3334   ieb.target = target;
3335
3336   ieb.dav_cache = dav_cache;
3337
3338   ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3339                                                 wcroot->abspath,
3340                                                 record_ancestor_abspath);
3341   ieb.recorded_repos_relpath = recorded_repos_relpath;
3342   ieb.recorded_peg_revision = recorded_peg_revision;
3343   ieb.recorded_revision = recorded_revision;
3344
3345   ieb.update_actual_props = update_actual_props;
3346   ieb.new_actual_props = new_actual_props;
3347
3348   ieb.keep_recorded_info = keep_recorded_info;
3349
3350   ieb.work_items = work_items;
3351
3352   SVN_WC__DB_WITH_TXN(
3353             insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3354             wcroot);
3355
3356   return SVN_NO_ERROR;
3357 }
3358
3359 svn_error_t *
3360 svn_wc__db_external_add_dir(svn_wc__db_t *db,
3361                             const char *local_abspath,
3362                             const char *wri_abspath,
3363                             const char *repos_root_url,
3364                             const char *repos_uuid,
3365                             const char *record_ancestor_abspath,
3366                             const char *recorded_repos_relpath,
3367                             svn_revnum_t recorded_peg_revision,
3368                             svn_revnum_t recorded_revision,
3369                             const svn_skel_t *work_items,
3370                             apr_pool_t *scratch_pool)
3371 {
3372   svn_wc__db_wcroot_t *wcroot;
3373   const char *local_relpath;
3374   insert_external_baton_t ieb;
3375
3376   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3377
3378   if (! wri_abspath)
3379     wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3380
3381   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3382                               wri_abspath, scratch_pool, scratch_pool));
3383   VERIFY_USABLE_WCROOT(wcroot);
3384
3385   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3386                                         record_ancestor_abspath));
3387
3388   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3389
3390   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3391
3392   blank_ieb(&ieb);
3393
3394   ieb.kind = svn_node_dir;
3395   ieb.presence = svn_wc__db_status_normal;
3396
3397   ieb.repos_root_url = repos_root_url;
3398   ieb.repos_uuid = repos_uuid;
3399
3400   ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3401                                                 wcroot->abspath,
3402                                                 record_ancestor_abspath);
3403   ieb.recorded_repos_relpath = recorded_repos_relpath;
3404   ieb.recorded_peg_revision = recorded_peg_revision;
3405   ieb.recorded_revision = recorded_revision;
3406
3407   ieb.work_items = work_items;
3408
3409   SVN_WC__DB_WITH_TXN(
3410             insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3411             wcroot);
3412
3413   return SVN_NO_ERROR;
3414 }
3415
3416 /* The body of svn_wc__db_external_remove(). */
3417 static svn_error_t *
3418 db_external_remove(const svn_skel_t *work_items,
3419                    svn_wc__db_wcroot_t *wcroot,
3420                    const char *local_relpath,
3421                    apr_pool_t *scratch_pool)
3422 {
3423   svn_sqlite__stmt_t *stmt;
3424
3425   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3426                                     STMT_DELETE_EXTERNAL));
3427   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3428   SVN_ERR(svn_sqlite__step_done(stmt));
3429
3430   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
3431
3432   /* ### What about actual? */
3433   return SVN_NO_ERROR;
3434 }
3435
3436 svn_error_t *
3437 svn_wc__db_external_remove(svn_wc__db_t *db,
3438                            const char *local_abspath,
3439                            const char *wri_abspath,
3440                            const svn_skel_t *work_items,
3441                            apr_pool_t *scratch_pool)
3442 {
3443   svn_wc__db_wcroot_t *wcroot;
3444   const char *local_relpath;
3445
3446   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3447
3448   if (! wri_abspath)
3449     wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3450
3451   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3452                               wri_abspath, scratch_pool, scratch_pool));
3453   VERIFY_USABLE_WCROOT(wcroot);
3454
3455   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3456
3457   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3458
3459   SVN_WC__DB_WITH_TXN(db_external_remove(work_items, wcroot, local_relpath,
3460                                          scratch_pool),
3461                       wcroot);
3462
3463   return SVN_NO_ERROR;
3464 }
3465
3466 svn_error_t *
3467 svn_wc__db_external_read(svn_wc__db_status_t *status,
3468                          svn_node_kind_t *kind,
3469                          const char **definining_abspath,
3470                          const char **repos_root_url,
3471                          const char **repos_uuid,
3472                          const char **recorded_repos_relpath,
3473                          svn_revnum_t *recorded_peg_revision,
3474                          svn_revnum_t *recorded_revision,
3475                          svn_wc__db_t *db,
3476                          const char *local_abspath,
3477                          const char *wri_abspath,
3478                          apr_pool_t *result_pool,
3479                          apr_pool_t *scratch_pool)
3480 {
3481   svn_wc__db_wcroot_t *wcroot;
3482   const char *local_relpath;
3483   svn_sqlite__stmt_t *stmt;
3484   svn_boolean_t have_info;
3485   svn_error_t *err = NULL;
3486   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3487
3488   if (! wri_abspath)
3489     wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3490
3491   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3492                               wri_abspath, scratch_pool, scratch_pool));
3493   VERIFY_USABLE_WCROOT(wcroot);
3494
3495   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3496
3497   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3498
3499   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3500                                     STMT_SELECT_EXTERNAL_INFO));
3501   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3502   SVN_ERR(svn_sqlite__step(&have_info, stmt));
3503
3504   if (have_info)
3505     {
3506       if (status)
3507         *status = svn_sqlite__column_token(stmt, 0, presence_map);
3508
3509       if (kind)
3510         *kind = svn_sqlite__column_token(stmt, 1, kind_map);
3511
3512       if (definining_abspath)
3513         {
3514           const char *record_relpath = svn_sqlite__column_text(stmt, 2, NULL);
3515
3516           *definining_abspath = svn_dirent_join(wcroot->abspath,
3517                                                 record_relpath, result_pool);
3518         }
3519
3520       if (repos_root_url || repos_uuid)
3521         {
3522           apr_int64_t repos_id;
3523
3524           repos_id = svn_sqlite__column_int64(stmt, 3);
3525
3526           err = svn_error_compose_create(
3527                         err,
3528                         svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
3529                                                     wcroot->sdb, repos_id,
3530                                                     result_pool));
3531         }
3532
3533       if (recorded_repos_relpath)
3534         *recorded_repos_relpath = svn_sqlite__column_text(stmt, 4,
3535                                                           result_pool);
3536
3537       if (recorded_peg_revision)
3538         *recorded_peg_revision = svn_sqlite__column_revnum(stmt, 5);
3539
3540       if (recorded_revision)
3541         *recorded_revision = svn_sqlite__column_revnum(stmt, 6);
3542     }
3543   else
3544     {
3545       err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3546                               _("The node '%s' is not an external."),
3547                               svn_dirent_local_style(local_abspath,
3548                                                      scratch_pool));
3549     }
3550
3551   return svn_error_trace(
3552                 svn_error_compose_create(err, svn_sqlite__reset(stmt)));
3553 }
3554
3555 svn_error_t *
3556 svn_wc__db_committable_externals_below(apr_array_header_t **externals,
3557                                        svn_wc__db_t *db,
3558                                        const char *local_abspath,
3559                                        svn_boolean_t immediates_only,
3560                                        apr_pool_t *result_pool,
3561                                        apr_pool_t *scratch_pool)
3562 {
3563   svn_wc__db_wcroot_t *wcroot;
3564   svn_sqlite__stmt_t *stmt;
3565   const char *local_relpath;
3566   svn_boolean_t have_row;
3567   svn_wc__committable_external_info_t *info;
3568   svn_node_kind_t db_kind;
3569   apr_array_header_t *result = NULL;
3570
3571   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3572
3573   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3574                               local_abspath, scratch_pool, scratch_pool));
3575   VERIFY_USABLE_WCROOT(wcroot);
3576
3577   SVN_ERR(svn_sqlite__get_statement(
3578                 &stmt, wcroot->sdb,
3579                 immediates_only
3580                     ? STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW
3581                     : STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW));
3582
3583   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3584
3585   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3586
3587   if (have_row)
3588     result = apr_array_make(result_pool, 0,
3589                             sizeof(svn_wc__committable_external_info_t *));
3590
3591   while (have_row)
3592     {
3593       info = apr_palloc(result_pool, sizeof(*info));
3594
3595       local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3596       info->local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
3597                                             result_pool);
3598
3599       db_kind = svn_sqlite__column_token(stmt, 1, kind_map);
3600       SVN_ERR_ASSERT(db_kind == svn_node_file || db_kind == svn_node_dir);
3601       info->kind = db_kind;
3602
3603       info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
3604       info->repos_root_url = svn_sqlite__column_text(stmt, 3, result_pool);
3605
3606       APR_ARRAY_PUSH(result, svn_wc__committable_external_info_t *) = info;
3607
3608       SVN_ERR(svn_sqlite__step(&have_row, stmt));
3609     }
3610
3611   *externals = result;
3612   return svn_error_trace(svn_sqlite__reset(stmt));
3613 }
3614
3615 svn_error_t *
3616 svn_wc__db_externals_defined_below(apr_hash_t **externals,
3617                                    svn_wc__db_t *db,
3618                                    const char *local_abspath,
3619                                    apr_pool_t *result_pool,
3620                                    apr_pool_t *scratch_pool)
3621 {
3622   svn_wc__db_wcroot_t *wcroot;
3623   svn_sqlite__stmt_t *stmt;
3624   const char *local_relpath;
3625   svn_boolean_t have_row;
3626
3627   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3628
3629   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3630                               local_abspath, scratch_pool, scratch_pool));
3631   VERIFY_USABLE_WCROOT(wcroot);
3632
3633   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3634                                     STMT_SELECT_EXTERNALS_DEFINED));
3635
3636   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3637
3638   *externals = apr_hash_make(result_pool);
3639   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3640
3641   while (have_row)
3642     {
3643       const char *def_local_relpath;
3644
3645       local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3646       def_local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3647
3648       svn_hash_sets(*externals,
3649                     svn_dirent_join(wcroot->abspath, local_relpath,
3650                                     result_pool),
3651                     svn_dirent_join(wcroot->abspath, def_local_relpath,
3652                                     result_pool));
3653
3654       SVN_ERR(svn_sqlite__step(&have_row, stmt));
3655     }
3656
3657   return svn_error_trace(svn_sqlite__reset(stmt));
3658 }
3659
3660 svn_error_t *
3661 svn_wc__db_externals_gather_definitions(apr_hash_t **externals,
3662                                         apr_hash_t **depths,
3663                                         svn_wc__db_t *db,
3664                                         const char *local_abspath,
3665                                         apr_pool_t *result_pool,
3666                                         apr_pool_t *scratch_pool)
3667 {
3668   svn_wc__db_wcroot_t *wcroot;
3669   svn_sqlite__stmt_t *stmt;
3670   const char *local_relpath;
3671   svn_boolean_t have_row;
3672   svn_error_t *err = NULL;
3673   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3674
3675   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3676
3677   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3678                               local_abspath, scratch_pool, iterpool));
3679   VERIFY_USABLE_WCROOT(wcroot);
3680
3681   *externals = apr_hash_make(result_pool);
3682   if (depths != NULL)
3683     *depths = apr_hash_make(result_pool);
3684
3685   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3686                                     STMT_SELECT_EXTERNAL_PROPERTIES));
3687
3688   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3689
3690   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3691
3692   while (have_row)
3693     {
3694       apr_hash_t *node_props;
3695       const char *external_value;
3696
3697       svn_pool_clear(iterpool);
3698       err = svn_sqlite__column_properties(&node_props, stmt, 0, iterpool,
3699                                           iterpool);
3700
3701       if (err)
3702         break;
3703
3704       external_value = svn_prop_get_value(node_props, SVN_PROP_EXTERNALS);
3705
3706       if (external_value)
3707         {
3708           const char *node_abspath;
3709           const char *node_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3710
3711           node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
3712                                          result_pool);
3713
3714           svn_hash_sets(*externals, node_abspath,
3715                         apr_pstrdup(result_pool, external_value));
3716
3717           if (depths)
3718             {
3719               svn_depth_t depth
3720                 = svn_sqlite__column_token_null(stmt, 2, depth_map,
3721                                                 svn_depth_unknown);
3722
3723               svn_hash_sets(*depths, node_abspath,
3724                             /* Use static string */
3725                             svn_token__to_word(depth_map, depth));
3726             }
3727         }
3728
3729       SVN_ERR(svn_sqlite__step(&have_row, stmt));
3730     }
3731
3732   svn_pool_destroy(iterpool);
3733
3734   return svn_error_trace(svn_error_compose_create(err,
3735                                                   svn_sqlite__reset(stmt)));
3736 }
3737
3738 /* Copy the ACTUAL data for SRC_RELPATH and tweak it to refer to DST_RELPATH.
3739    The new ACTUAL data won't have any conflicts. */
3740 static svn_error_t *
3741 copy_actual(svn_wc__db_wcroot_t *src_wcroot,
3742             const char *src_relpath,
3743             svn_wc__db_wcroot_t *dst_wcroot,
3744             const char *dst_relpath,
3745             apr_pool_t *scratch_pool)
3746 {
3747   svn_sqlite__stmt_t *stmt;
3748   svn_boolean_t have_row;
3749
3750   SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
3751                                     STMT_SELECT_ACTUAL_NODE));
3752   SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
3753   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3754   if (have_row)
3755     {
3756       apr_size_t props_size;
3757       const char *changelist;
3758       const char *properties;
3759
3760       /* Skipping conflict data... */
3761       changelist = svn_sqlite__column_text(stmt, 0, scratch_pool);
3762       /* No need to parse the properties when simply copying. */
3763       properties = svn_sqlite__column_blob(stmt, 1, &props_size, scratch_pool);
3764
3765       if (changelist || properties)
3766         {
3767           SVN_ERR(svn_sqlite__reset(stmt));
3768
3769           SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
3770                                             STMT_INSERT_ACTUAL_NODE));
3771           SVN_ERR(svn_sqlite__bindf(stmt, "issbs",
3772                                     dst_wcroot->wc_id, dst_relpath,
3773                                 svn_relpath_dirname(dst_relpath, scratch_pool),
3774                                     properties, props_size, changelist));
3775           SVN_ERR(svn_sqlite__step(&have_row, stmt));
3776         }
3777     }
3778   SVN_ERR(svn_sqlite__reset(stmt));
3779
3780   return SVN_NO_ERROR;
3781 }
3782
3783 /* Helper for svn_wc__db_op_copy to handle copying from one db to
3784    another */
3785 static svn_error_t *
3786 cross_db_copy(svn_wc__db_wcroot_t *src_wcroot,
3787               const char *src_relpath,
3788               svn_wc__db_wcroot_t *dst_wcroot,
3789               const char *dst_relpath,
3790               svn_wc__db_status_t dst_status,
3791               int dst_op_depth,
3792               int dst_np_op_depth,
3793               svn_node_kind_t kind,
3794               const apr_array_header_t *children,
3795               apr_int64_t copyfrom_id,
3796               const char *copyfrom_relpath,
3797               svn_revnum_t copyfrom_rev,
3798               apr_pool_t *scratch_pool)
3799 {
3800   insert_working_baton_t iwb;
3801   svn_revnum_t changed_rev;
3802   apr_time_t changed_date;
3803   const char *changed_author;
3804   const svn_checksum_t *checksum;
3805   apr_hash_t *props;
3806   svn_depth_t depth;
3807
3808   SVN_ERR_ASSERT(kind == svn_node_file
3809                  || kind == svn_node_dir
3810                  );
3811
3812   SVN_ERR(read_info(NULL, NULL, NULL, NULL, NULL,
3813                     &changed_rev, &changed_date, &changed_author, &depth,
3814                     &checksum, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3815                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3816                     src_wcroot, src_relpath, scratch_pool, scratch_pool));
3817
3818   if (dst_status != svn_wc__db_status_not_present
3819       && dst_status != svn_wc__db_status_excluded
3820       && dst_status != svn_wc__db_status_server_excluded)
3821     {
3822       SVN_ERR(db_read_pristine_props(&props, src_wcroot, src_relpath, FALSE,
3823                                      scratch_pool, scratch_pool));
3824     }
3825   else
3826     props = NULL;
3827
3828   blank_iwb(&iwb);
3829   iwb.presence = dst_status;
3830   iwb.kind = kind;
3831
3832   iwb.props = props;
3833   iwb.changed_rev = changed_rev;
3834   iwb.changed_date = changed_date;
3835   iwb.changed_author = changed_author;
3836   iwb.original_repos_id = copyfrom_id;
3837   iwb.original_repos_relpath = copyfrom_relpath;
3838   iwb.original_revnum = copyfrom_rev;
3839   iwb.moved_here = FALSE;
3840
3841   iwb.op_depth = dst_op_depth;
3842
3843   iwb.checksum = checksum;
3844   iwb.children = children;
3845   iwb.depth = depth;
3846
3847   iwb.not_present_op_depth = dst_np_op_depth;
3848
3849   SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath, scratch_pool));
3850
3851   SVN_ERR(copy_actual(src_wcroot, src_relpath,
3852                       dst_wcroot, dst_relpath, scratch_pool));
3853
3854   return SVN_NO_ERROR;
3855 }
3856
3857 /* Helper for scan_deletion_txn. Extracts the moved-to information, if
3858    any, from STMT.  Sets *SCAN to FALSE if moved-to was available. */
3859 static svn_error_t *
3860 get_moved_to(const char **moved_to_relpath_p,
3861              const char **moved_to_op_root_relpath_p,
3862              svn_boolean_t *scan,
3863              svn_sqlite__stmt_t *stmt,
3864              const char *current_relpath,
3865              svn_wc__db_wcroot_t *wcroot,
3866              const char *local_relpath,
3867              apr_pool_t *result_pool,
3868              apr_pool_t *scratch_pool)
3869 {
3870   const char *moved_to_relpath = svn_sqlite__column_text(stmt, 3, NULL);
3871
3872   if (moved_to_relpath)
3873     {
3874       const char *moved_to_op_root_relpath = moved_to_relpath;
3875
3876       if (strcmp(current_relpath, local_relpath))
3877         {
3878           /* LOCAL_RELPATH is a child inside the move op-root. */
3879           const char *moved_child_relpath;
3880
3881           /* The CURRENT_RELPATH is the op_root of the delete-half of
3882            * the move. LOCAL_RELPATH is a child that was moved along.
3883            * Compute the child's new location within the move target. */
3884           moved_child_relpath = svn_relpath_skip_ancestor(current_relpath,
3885                                                           local_relpath);
3886           SVN_ERR_ASSERT(moved_child_relpath &&
3887                          strlen(moved_child_relpath) > 0);
3888           moved_to_relpath = svn_relpath_join(moved_to_op_root_relpath,
3889                                               moved_child_relpath,
3890                                               result_pool);
3891         }
3892
3893       if (moved_to_op_root_relpath && moved_to_op_root_relpath_p)
3894         *moved_to_op_root_relpath_p
3895           = apr_pstrdup(result_pool, moved_to_op_root_relpath);
3896
3897       if (moved_to_relpath && moved_to_relpath_p)
3898         *moved_to_relpath_p
3899           = apr_pstrdup(result_pool, moved_to_relpath);
3900
3901       *scan = FALSE;
3902     }
3903
3904   return SVN_NO_ERROR;
3905 }
3906
3907
3908 /* The body of svn_wc__db_scan_deletion().
3909  */
3910 static svn_error_t *
3911 scan_deletion_txn(const char **base_del_relpath,
3912                   const char **moved_to_relpath,
3913                   const char **work_del_relpath,
3914                   const char **moved_to_op_root_relpath,
3915                   svn_wc__db_wcroot_t *wcroot,
3916                   const char *local_relpath,
3917                   apr_pool_t *result_pool,
3918                   apr_pool_t *scratch_pool)
3919 {
3920   const char *current_relpath = local_relpath;
3921   svn_sqlite__stmt_t *stmt;
3922   svn_wc__db_status_t work_presence;
3923   svn_boolean_t have_row, scan, have_base;
3924   int op_depth;
3925
3926   /* Initialize all the OUT parameters.  */
3927   if (base_del_relpath != NULL)
3928     *base_del_relpath = NULL;
3929   if (moved_to_relpath != NULL)
3930     *moved_to_relpath = NULL;
3931   if (work_del_relpath != NULL)
3932     *work_del_relpath = NULL;
3933   if (moved_to_op_root_relpath != NULL)
3934     *moved_to_op_root_relpath = NULL;
3935
3936   /* If looking for moved-to info then we need to scan every path
3937      until we find it.  If not looking for moved-to we only need to
3938      check op-roots and parents of op-roots. */
3939   scan = (moved_to_op_root_relpath || moved_to_relpath);
3940
3941   SVN_ERR(svn_sqlite__get_statement(
3942                     &stmt, wcroot->sdb,
3943                     scan ? STMT_SELECT_DELETION_INFO_SCAN
3944                          : STMT_SELECT_DELETION_INFO));
3945
3946   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
3947   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3948   if (!have_row)
3949     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
3950                              _("The node '%s' was not found."),
3951                              path_for_error_message(wcroot, local_relpath,
3952                                                     scratch_pool));
3953
3954   work_presence = svn_sqlite__column_token(stmt, 1, presence_map);
3955   have_base = !svn_sqlite__column_is_null(stmt, 0);
3956   if (work_presence != svn_wc__db_status_not_present
3957       && work_presence != svn_wc__db_status_base_deleted)
3958     return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
3959                              svn_sqlite__reset(stmt),
3960                              _("Expected node '%s' to be deleted."),
3961                              path_for_error_message(wcroot, local_relpath,
3962                                                     scratch_pool));
3963
3964   op_depth = svn_sqlite__column_int(stmt, 2);
3965
3966   /* Special case: LOCAL_RELPATH not-present within a WORKING tree, we
3967      treat this as an op-root.  At commit time we need to explicitly
3968      delete such nodes otherwise they will be present in the
3969      repository copy. */
3970   if (work_presence == svn_wc__db_status_not_present
3971       && work_del_relpath && !*work_del_relpath)
3972     {
3973       *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
3974
3975       if (!scan && !base_del_relpath)
3976         {
3977           /* We have all we need, exit early */
3978           SVN_ERR(svn_sqlite__reset(stmt));
3979           return SVN_NO_ERROR;
3980         }
3981     }
3982
3983
3984   while (TRUE)
3985     {
3986       svn_error_t *err;
3987       const char *parent_relpath;
3988       int current_depth = relpath_depth(current_relpath);
3989
3990       /* Step CURRENT_RELPATH to op-root */
3991
3992       while (TRUE)
3993         {
3994           if (scan)
3995             {
3996               err = get_moved_to(moved_to_relpath, moved_to_op_root_relpath,
3997                                  &scan, stmt, current_relpath,
3998                                  wcroot, local_relpath,
3999                                  result_pool, scratch_pool);
4000               if (err || (!scan
4001                           && !base_del_relpath
4002                           && !work_del_relpath))
4003                 {
4004                   /* We have all we need (or an error occurred) */
4005                   SVN_ERR(svn_sqlite__reset(stmt));
4006                   return svn_error_trace(err);
4007                 }
4008             }
4009
4010           if (current_depth <= op_depth)
4011             break;
4012
4013           current_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4014           --current_depth;
4015
4016           if (scan || current_depth == op_depth)
4017             {
4018               SVN_ERR(svn_sqlite__reset(stmt));
4019               SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
4020                                         current_relpath));
4021               SVN_ERR(svn_sqlite__step(&have_row, stmt));
4022               SVN_ERR_ASSERT(have_row);
4023               have_base = !svn_sqlite__column_is_null(stmt, 0);
4024             }
4025         }
4026       SVN_ERR(svn_sqlite__reset(stmt));
4027
4028       /* Now CURRENT_RELPATH is an op-root, have a look at the parent. */
4029
4030       SVN_ERR_ASSERT(current_relpath[0] != '\0'); /* Catch invalid data */
4031       parent_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4032       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4033       SVN_ERR(svn_sqlite__step(&have_row, stmt));
4034       if (!have_row)
4035         {
4036           /* No row means no WORKING node which mean we just fell off
4037              the WORKING tree, so CURRENT_RELPATH is the op-root
4038              closest to the wc root. */
4039           if (have_base && base_del_relpath)
4040             *base_del_relpath = apr_pstrdup(result_pool, current_relpath);
4041           break;
4042         }
4043
4044       /* Still in the WORKING tree so the first time we get here
4045          CURRENT_RELPATH is a delete op-root in the WORKING tree. */
4046       if (work_del_relpath && !*work_del_relpath)
4047         {
4048           *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
4049
4050           if (!scan && !base_del_relpath)
4051             break; /* We have all we need */
4052         }
4053
4054       current_relpath = parent_relpath;
4055       op_depth = svn_sqlite__column_int(stmt, 2);
4056       have_base = !svn_sqlite__column_is_null(stmt, 0);
4057     }
4058
4059   SVN_ERR(svn_sqlite__reset(stmt));
4060
4061   return SVN_NO_ERROR;
4062 }
4063
4064 svn_error_t *
4065 svn_wc__db_scan_deletion(const char **base_del_abspath,
4066                          const char **moved_to_abspath,
4067                          const char **work_del_abspath,
4068                          const char **moved_to_op_root_abspath,
4069                          svn_wc__db_t *db,
4070                          const char *local_abspath,
4071                          apr_pool_t *result_pool,
4072                          apr_pool_t *scratch_pool)
4073 {
4074   svn_wc__db_wcroot_t *wcroot;
4075   const char *local_relpath;
4076   const char *base_del_relpath, *moved_to_relpath, *work_del_relpath;
4077   const char *moved_to_op_root_relpath;
4078
4079   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4080
4081   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4082                               local_abspath, scratch_pool, scratch_pool));
4083   VERIFY_USABLE_WCROOT(wcroot);
4084
4085   SVN_WC__DB_WITH_TXN(
4086     scan_deletion_txn(&base_del_relpath, &moved_to_relpath,
4087                       &work_del_relpath, &moved_to_op_root_relpath,
4088                       wcroot, local_relpath, result_pool, scratch_pool),
4089     wcroot);
4090
4091   if (base_del_abspath)
4092     {
4093       *base_del_abspath = (base_del_relpath
4094                            ? svn_dirent_join(wcroot->abspath,
4095                                              base_del_relpath, result_pool)
4096                            : NULL);
4097     }
4098   if (moved_to_abspath)
4099     {
4100       *moved_to_abspath = (moved_to_relpath
4101                            ? svn_dirent_join(wcroot->abspath,
4102                                              moved_to_relpath, result_pool)
4103                            : NULL);
4104     }
4105   if (work_del_abspath)
4106     {
4107       *work_del_abspath = (work_del_relpath
4108                            ? svn_dirent_join(wcroot->abspath,
4109                                              work_del_relpath, result_pool)
4110                            : NULL);
4111     }
4112   if (moved_to_op_root_abspath)
4113     {
4114       *moved_to_op_root_abspath = (moved_to_op_root_relpath
4115                            ? svn_dirent_join(wcroot->abspath,
4116                                              moved_to_op_root_relpath,
4117                                              result_pool)
4118                            : NULL);
4119     }
4120
4121   return SVN_NO_ERROR;
4122 }
4123
4124
4125 /* Set *COPYFROM_ID, *COPYFROM_RELPATH, *COPYFROM_REV to the values
4126    appropriate for the copy. Also return *STATUS, *KIND and *HAVE_WORK, *OP_ROOT
4127    since they are available.  This is a helper for
4128    svn_wc__db_op_copy. */
4129 static svn_error_t *
4130 get_info_for_copy(apr_int64_t *copyfrom_id,
4131                   const char **copyfrom_relpath,
4132                   svn_revnum_t *copyfrom_rev,
4133                   svn_wc__db_status_t *status,
4134                   svn_node_kind_t *kind,
4135                   svn_boolean_t *op_root,
4136                   svn_wc__db_wcroot_t *src_wcroot,
4137                   const char *local_relpath,
4138                   svn_wc__db_wcroot_t *dst_wcroot,
4139                   apr_pool_t *result_pool,
4140                   apr_pool_t *scratch_pool)
4141 {
4142   const char *repos_relpath;
4143   svn_revnum_t revision;
4144   svn_wc__db_status_t node_status;
4145   apr_int64_t repos_id;
4146   svn_boolean_t is_op_root;
4147
4148   SVN_ERR(read_info(&node_status, kind, &revision, &repos_relpath, &repos_id,
4149                     NULL, NULL, NULL, NULL, NULL, NULL, copyfrom_relpath,
4150                     copyfrom_id, copyfrom_rev, NULL, NULL, NULL, NULL,
4151                     NULL, &is_op_root, NULL, NULL,
4152                     NULL /* have_base */,
4153                     NULL /* have_more_work */,
4154                     NULL /* have_work */,
4155                     src_wcroot, local_relpath, result_pool, scratch_pool));
4156
4157   if (op_root)
4158     *op_root = is_op_root;
4159
4160   if (node_status == svn_wc__db_status_excluded)
4161     {
4162       /* The parent cannot be excluded, so look at the parent and then
4163          adjust the relpath */
4164       const char *parent_relpath, *base_name;
4165
4166       svn_dirent_split(&parent_relpath, &base_name, local_relpath,
4167                        scratch_pool);
4168       SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev,
4169                                 NULL, NULL, NULL,
4170                                 src_wcroot, parent_relpath, dst_wcroot,
4171                                 scratch_pool, scratch_pool));
4172       if (*copyfrom_relpath)
4173         *copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, base_name,
4174                                              result_pool);
4175     }
4176   else if (node_status == svn_wc__db_status_added)
4177     {
4178       SVN_ERR(scan_addition(&node_status, NULL, NULL, NULL, NULL, NULL, NULL,
4179                             NULL, NULL, NULL, src_wcroot, local_relpath,
4180                             scratch_pool, scratch_pool));
4181     }
4182   else if (node_status == svn_wc__db_status_deleted && is_op_root)
4183     {
4184       const char *base_del_relpath, *work_del_relpath;
4185
4186       SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
4187                                 &work_del_relpath,
4188                                 NULL, src_wcroot, local_relpath,
4189                                 scratch_pool, scratch_pool));
4190       if (work_del_relpath)
4191         {
4192           const char *op_root_relpath;
4193           const char *parent_del_relpath = svn_relpath_dirname(work_del_relpath,
4194                                                                scratch_pool);
4195
4196           /* Similar to, but not the same as, the _scan_addition and
4197              _join above.  Can we use get_copyfrom here? */
4198           SVN_ERR(scan_addition(NULL, &op_root_relpath,
4199                                 NULL, NULL, /* repos_* */
4200                                 copyfrom_relpath, copyfrom_id, copyfrom_rev,
4201                                 NULL, NULL, NULL,
4202                                 src_wcroot, parent_del_relpath,
4203                                 scratch_pool, scratch_pool));
4204           *copyfrom_relpath
4205             = svn_relpath_join(*copyfrom_relpath,
4206                                svn_relpath_skip_ancestor(op_root_relpath,
4207                                                          local_relpath),
4208                                result_pool);
4209         }
4210       else if (base_del_relpath)
4211         {
4212           SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, copyfrom_rev,
4213                                                     copyfrom_relpath,
4214                                                     copyfrom_id, NULL, NULL,
4215                                                     NULL, NULL, NULL, NULL,
4216                                                     NULL, NULL, NULL, NULL,
4217                                                     src_wcroot, local_relpath,
4218                                                     result_pool,
4219                                                     scratch_pool));
4220         }
4221       else
4222         SVN_ERR_MALFUNCTION();
4223     }
4224   else if (node_status == svn_wc__db_status_deleted)
4225     {
4226       /* Keep original_* from read_info() to allow seeing the difference
4227          between base-deleted and not present */
4228     }
4229   else
4230     {
4231       *copyfrom_relpath = repos_relpath;
4232       *copyfrom_rev = revision;
4233       *copyfrom_id = repos_id;
4234     }
4235
4236   if (status)
4237     *status = node_status;
4238
4239   if (src_wcroot != dst_wcroot && *copyfrom_relpath)
4240     {
4241       const char *repos_root_url;
4242       const char *repos_uuid;
4243
4244       /* Pass the right repos-id for the destination db. We can't just use
4245          the id of the source database, as this value can change after
4246          relocation (and perhaps also when we start storing multiple
4247          working copies in a single db)! */
4248
4249       SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
4250                                           src_wcroot->sdb, *copyfrom_id,
4251                                           scratch_pool));
4252
4253       SVN_ERR(create_repos_id(copyfrom_id, repos_root_url, repos_uuid,
4254                               dst_wcroot->sdb, scratch_pool));
4255     }
4256
4257   return SVN_NO_ERROR;
4258 }
4259
4260
4261 /* Set *OP_DEPTH to the highest op depth of WCROOT:LOCAL_RELPATH. */
4262 static svn_error_t *
4263 op_depth_of(int *op_depth,
4264             svn_wc__db_wcroot_t *wcroot,
4265             const char *local_relpath)
4266 {
4267   svn_sqlite__stmt_t *stmt;
4268   svn_boolean_t have_row;
4269
4270   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4271                                     STMT_SELECT_NODE_INFO));
4272   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4273   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4274   SVN_ERR_ASSERT(have_row);
4275   *op_depth = svn_sqlite__column_int(stmt, 0);
4276   SVN_ERR(svn_sqlite__reset(stmt));
4277
4278   return SVN_NO_ERROR;
4279 }
4280
4281
4282 /* Determine at which OP_DEPTH a copy of COPYFROM_REPOS_ID, COPYFROM_RELPATH at
4283    revision COPYFROM_REVISION should be inserted as LOCAL_RELPATH. Do this
4284    by checking if this would be a direct child of a copy of its parent
4285    directory. If it is then set *OP_DEPTH to the op_depth of its parent.
4286
4287    If the node is not a direct copy at the same revision of the parent
4288    *NP_OP_DEPTH will be set to the op_depth of the parent when a not-present
4289    node should be inserted at this op_depth. This will be the case when the
4290    parent already defined an incomplete child with the same name. Otherwise
4291    *NP_OP_DEPTH will be set to -1.
4292
4293    If the parent node is not the parent of the to be copied node, then
4294    *OP_DEPTH will be set to the proper op_depth for a new operation root.
4295
4296    Set *PARENT_OP_DEPTH to the op_depth of the parent.
4297
4298  */
4299 static svn_error_t *
4300 op_depth_for_copy(int *op_depth,
4301                   int *np_op_depth,
4302                   int *parent_op_depth,
4303                   apr_int64_t copyfrom_repos_id,
4304                   const char *copyfrom_relpath,
4305                   svn_revnum_t copyfrom_revision,
4306                   svn_wc__db_wcroot_t *wcroot,
4307                   const char *local_relpath,
4308                   apr_pool_t *scratch_pool)
4309 {
4310   const char *parent_relpath, *name;
4311   svn_sqlite__stmt_t *stmt;
4312   svn_boolean_t have_row;
4313   int incomplete_op_depth = -1;
4314   int min_op_depth = 1; /* Never touch BASE */
4315
4316   *op_depth = relpath_depth(local_relpath);
4317   *np_op_depth = -1;
4318
4319   svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool);
4320   *parent_op_depth = relpath_depth(parent_relpath);
4321
4322   if (!copyfrom_relpath)
4323     return SVN_NO_ERROR;
4324
4325   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4326                                     STMT_SELECT_WORKING_NODE));
4327   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4328   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4329   if (have_row)
4330     {
4331       svn_wc__db_status_t status = svn_sqlite__column_token(stmt, 1,
4332                                                             presence_map);
4333
4334       min_op_depth = svn_sqlite__column_int(stmt, 0);
4335       if (status == svn_wc__db_status_incomplete)
4336         incomplete_op_depth = min_op_depth;
4337     }
4338   SVN_ERR(svn_sqlite__reset(stmt));
4339
4340   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4341                                     STMT_SELECT_WORKING_NODE));
4342   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4343   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4344   if (have_row)
4345     {
4346       svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 1,
4347                                                               presence_map);
4348
4349       *parent_op_depth = svn_sqlite__column_int(stmt, 0);
4350       if (*parent_op_depth < min_op_depth)
4351         {
4352           /* We want to create a copy; not overwrite the lower layers */
4353           SVN_ERR(svn_sqlite__reset(stmt));
4354           return SVN_NO_ERROR;
4355         }
4356
4357       /* You can only add children below a node that exists.
4358          In WORKING that must be status added, which is represented
4359          as presence normal */
4360       SVN_ERR_ASSERT(presence == svn_wc__db_status_normal);
4361
4362       if ((incomplete_op_depth < 0)
4363           || (incomplete_op_depth == *parent_op_depth))
4364         {
4365           apr_int64_t parent_copyfrom_repos_id
4366             = svn_sqlite__column_int64(stmt, 10);
4367           const char *parent_copyfrom_relpath
4368             = svn_sqlite__column_text(stmt, 11, NULL);
4369           svn_revnum_t parent_copyfrom_revision
4370             = svn_sqlite__column_revnum(stmt, 12);
4371
4372           if (parent_copyfrom_repos_id == copyfrom_repos_id)
4373             {
4374               if (copyfrom_revision == parent_copyfrom_revision
4375                   && !strcmp(copyfrom_relpath,
4376                              svn_relpath_join(parent_copyfrom_relpath, name,
4377                                               scratch_pool)))
4378                 *op_depth = *parent_op_depth;
4379               else if (incomplete_op_depth > 0)
4380                 *np_op_depth = incomplete_op_depth;
4381             }
4382         }
4383     }
4384   SVN_ERR(svn_sqlite__reset(stmt));
4385
4386   return SVN_NO_ERROR;
4387 }
4388
4389
4390 /* Like svn_wc__db_op_copy(), but with WCROOT+LOCAL_RELPATH
4391  * instead of DB+LOCAL_ABSPATH. A non-zero MOVE_OP_DEPTH implies that the
4392  * copy operation is part of a move, and indicates the op-depth of the
4393  * move destination op-root. */
4394 static svn_error_t *
4395 db_op_copy(svn_wc__db_wcroot_t *src_wcroot,
4396            const char *src_relpath,
4397            svn_wc__db_wcroot_t *dst_wcroot,
4398            const char *dst_relpath,
4399            const svn_skel_t *work_items,
4400            int move_op_depth,
4401            apr_pool_t *scratch_pool)
4402 {
4403   const char *copyfrom_relpath;
4404   svn_revnum_t copyfrom_rev;
4405   svn_wc__db_status_t status;
4406   svn_wc__db_status_t dst_presence;
4407   svn_boolean_t op_root;
4408   apr_int64_t copyfrom_id;
4409   int dst_op_depth;
4410   int dst_np_op_depth;
4411   int dst_parent_op_depth;
4412   svn_node_kind_t kind;
4413   const apr_array_header_t *children;
4414
4415   SVN_ERR(get_info_for_copy(&copyfrom_id, &copyfrom_relpath, &copyfrom_rev,
4416                             &status, &kind, &op_root,
4417                             src_wcroot, src_relpath, dst_wcroot,
4418                             scratch_pool, scratch_pool));
4419
4420   SVN_ERR(op_depth_for_copy(&dst_op_depth, &dst_np_op_depth,
4421                             &dst_parent_op_depth,
4422                             copyfrom_id, copyfrom_relpath, copyfrom_rev,
4423                             dst_wcroot, dst_relpath, scratch_pool));
4424
4425   SVN_ERR_ASSERT(kind == svn_node_file || kind == svn_node_dir);
4426
4427   /* ### New status, not finished, see notes/wc-ng/copying */
4428   switch (status)
4429     {
4430     case svn_wc__db_status_normal:
4431     case svn_wc__db_status_added:
4432     case svn_wc__db_status_moved_here:
4433     case svn_wc__db_status_copied:
4434       dst_presence = svn_wc__db_status_normal;
4435       break;
4436     case svn_wc__db_status_deleted:
4437       if (op_root)
4438         {
4439           /* If the lower layer is already shadowcopied we can skip adding
4440              a not present node. */
4441           svn_error_t *err;
4442           svn_wc__db_status_t dst_status;
4443
4444           err = read_info(&dst_status, NULL, NULL, NULL, NULL, NULL, NULL,
4445                           NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4446                           NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4447                           dst_wcroot, dst_relpath, scratch_pool, scratch_pool);
4448
4449           if (err)
4450             {
4451               if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
4452                 svn_error_clear(err);
4453               else
4454                 return svn_error_trace(err);
4455             }
4456           else if (dst_status == svn_wc__db_status_deleted)
4457             {
4458               /* Node is already deleted; skip the NODES work, but do
4459                  install wq items if requested */
4460               SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4461                                      scratch_pool));
4462               return SVN_NO_ERROR;
4463             }
4464         }
4465       else
4466         {
4467           /* This node is either a not-present node (which should be copied), or
4468              a base-delete of some lower layer (which shouldn't).
4469              Subversion <= 1.7 always added a not-present node here, which is
4470              safe (as it postpones the hard work until commit time and then we
4471              ask the repository), but it breaks some move scenarios.
4472              */
4473
4474            if (! copyfrom_relpath)
4475              {
4476                SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4477                                      scratch_pool));
4478                return SVN_NO_ERROR;
4479              }
4480
4481            /* Fall through. Install not present node */
4482         }
4483     case svn_wc__db_status_not_present:
4484     case svn_wc__db_status_excluded:
4485       /* These presence values should not create a new op depth */
4486       if (dst_np_op_depth > 0)
4487         {
4488           dst_op_depth = dst_np_op_depth;
4489           dst_np_op_depth = -1;
4490         }
4491       if (status == svn_wc__db_status_excluded)
4492         dst_presence = svn_wc__db_status_excluded;
4493       else
4494         dst_presence = svn_wc__db_status_not_present;
4495       break;
4496     case svn_wc__db_status_server_excluded:
4497       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4498                                _("Cannot copy '%s' excluded by server"),
4499                                path_for_error_message(src_wcroot,
4500                                                       src_relpath,
4501                                                       scratch_pool));
4502     default:
4503       /* Perhaps we should allow incomplete to incomplete? We can't
4504          avoid incomplete working nodes as one step in copying a
4505          directory is to add incomplete children. */
4506       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4507                                _("Cannot handle status of '%s'"),
4508                                path_for_error_message(src_wcroot,
4509                                                       src_relpath,
4510                                                       scratch_pool));
4511     }
4512
4513   if (kind == svn_node_dir)
4514     {
4515       int src_op_depth;
4516
4517       SVN_ERR(op_depth_of(&src_op_depth, src_wcroot, src_relpath));
4518       SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
4519                                    src_op_depth, scratch_pool, scratch_pool));
4520     }
4521   else
4522     children = NULL;
4523
4524   if (src_wcroot == dst_wcroot)
4525     {
4526       svn_sqlite__stmt_t *stmt;
4527       const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
4528                                                            scratch_pool);
4529
4530       SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
4531                                         STMT_INSERT_WORKING_NODE_COPY_FROM));
4532
4533       SVN_ERR(svn_sqlite__bindf(stmt, "issdst",
4534                     src_wcroot->wc_id, src_relpath,
4535                     dst_relpath,
4536                     dst_op_depth,
4537                     dst_parent_relpath,
4538                     presence_map, dst_presence));
4539
4540       if (move_op_depth > 0)
4541         {
4542           if (relpath_depth(dst_relpath) == move_op_depth)
4543             {
4544               /* We're moving the root of the move operation.
4545                *
4546                * When an added node or the op-root of a copy is moved,
4547                * there is no 'moved-from' corresponding to the moved-here
4548                * node. So the net effect is the same as copy+delete.
4549                * Perform a normal copy operation in these cases. */
4550               if (!(status == svn_wc__db_status_added ||
4551                     (status == svn_wc__db_status_copied && op_root)))
4552                 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4553             }
4554           else
4555             {
4556               svn_sqlite__stmt_t *info_stmt;
4557               svn_boolean_t have_row;
4558
4559               /* We're moving a child along with the root of the move.
4560                *
4561                * Set moved-here depending on dst_parent, propagating the
4562                * above decision to moved-along children at the same op_depth.
4563                * We can't use scan_addition() to detect moved-here because
4564                * the delete-half of the move might not yet exist. */
4565               SVN_ERR(svn_sqlite__get_statement(&info_stmt, dst_wcroot->sdb,
4566                                                 STMT_SELECT_NODE_INFO));
4567               SVN_ERR(svn_sqlite__bindf(info_stmt, "is", dst_wcroot->wc_id,
4568                                         dst_parent_relpath));
4569               SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4570               SVN_ERR_ASSERT(have_row);
4571               if (svn_sqlite__column_boolean(info_stmt, 15) &&
4572                   dst_op_depth == dst_parent_op_depth)
4573                 {
4574                   SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4575                   SVN_ERR(svn_sqlite__reset(info_stmt));
4576                 }
4577               else
4578                 {
4579                   SVN_ERR(svn_sqlite__reset(info_stmt));
4580
4581                   /* If the child has been moved into the tree we're moving,
4582                    * keep its moved-here bit set. */
4583                   SVN_ERR(svn_sqlite__get_statement(&info_stmt,
4584                                                     dst_wcroot->sdb,
4585                                                     STMT_SELECT_NODE_INFO));
4586                   SVN_ERR(svn_sqlite__bindf(info_stmt, "is",
4587                                             dst_wcroot->wc_id, src_relpath));
4588                   SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4589                   SVN_ERR_ASSERT(have_row);
4590                   if (svn_sqlite__column_boolean(info_stmt, 15))
4591                     SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4592                   SVN_ERR(svn_sqlite__reset(info_stmt));
4593                 }
4594             }
4595         }
4596
4597       SVN_ERR(svn_sqlite__step_done(stmt));
4598
4599       /* ### Copying changelist is OK for a move but what about a copy? */
4600       SVN_ERR(copy_actual(src_wcroot, src_relpath,
4601                           dst_wcroot, dst_relpath, scratch_pool));
4602
4603       if (dst_np_op_depth > 0)
4604         {
4605           /* We introduce a not-present node at the parent's op_depth to
4606              properly start a new op-depth at our own op_depth. This marks
4607              us as an op_root for commit and allows reverting just this
4608              operation */
4609
4610           SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
4611                                             STMT_INSERT_NODE));
4612           SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
4613                                     src_wcroot->wc_id, dst_relpath,
4614                                     dst_np_op_depth, dst_parent_relpath,
4615                                     copyfrom_id, copyfrom_relpath,
4616                                     copyfrom_rev,
4617                                     presence_map,
4618                                        svn_wc__db_status_not_present,
4619                                     /* NULL */
4620                                     kind_map, kind));
4621
4622           SVN_ERR(svn_sqlite__step_done(stmt));
4623         }
4624       /* Insert incomplete children, if relevant.
4625          The children are part of the same op and so have the same op_depth.
4626          (The only time we'd want a different depth is during a recursive
4627          simple add, but we never insert children here during a simple add.) */
4628       if (kind == svn_node_dir
4629           && dst_presence == svn_wc__db_status_normal)
4630         SVN_ERR(insert_incomplete_children(
4631                   dst_wcroot->sdb,
4632                   dst_wcroot->wc_id,
4633                   dst_relpath,
4634                   copyfrom_id,
4635                   copyfrom_relpath,
4636                   copyfrom_rev,
4637                   children,
4638                   dst_op_depth,
4639                   scratch_pool));
4640     }
4641   else
4642     {
4643       SVN_ERR(cross_db_copy(src_wcroot, src_relpath, dst_wcroot,
4644                             dst_relpath, dst_presence, dst_op_depth,
4645                             dst_np_op_depth, kind,
4646                             children, copyfrom_id, copyfrom_relpath,
4647                             copyfrom_rev, scratch_pool));
4648     }
4649
4650   SVN_ERR(add_work_items(dst_wcroot->sdb, work_items, scratch_pool));
4651
4652   return SVN_NO_ERROR;
4653 }
4654
4655 /* Baton for passing args to op_copy_txn(). */
4656 struct op_copy_baton
4657 {
4658   svn_wc__db_wcroot_t *src_wcroot;
4659   const char *src_relpath;
4660
4661   svn_wc__db_wcroot_t *dst_wcroot;
4662   const char *dst_relpath;
4663
4664   const svn_skel_t *work_items;
4665
4666   svn_boolean_t is_move;
4667   const char *dst_op_root_relpath;
4668 };
4669
4670 /* Helper for svn_wc__db_op_copy().
4671  *
4672  * Implements svn_sqlite__transaction_callback_t. */
4673 static svn_error_t *
4674 op_copy_txn(void * baton,
4675             svn_sqlite__db_t *sdb,
4676             apr_pool_t *scratch_pool)
4677 {
4678   struct op_copy_baton *ocb = baton;
4679   int move_op_depth;
4680
4681   if (sdb != ocb->dst_wcroot->sdb)
4682     {
4683        /* Source and destination databases differ; so also start a lock
4684           in the destination database, by calling ourself in a lock. */
4685
4686       return svn_error_trace(
4687                         svn_sqlite__with_lock(ocb->dst_wcroot->sdb,
4688                                               op_copy_txn, ocb, scratch_pool));
4689     }
4690
4691   /* From this point we can assume a lock in the src and dst databases */
4692
4693   if (ocb->is_move)
4694     move_op_depth = relpath_depth(ocb->dst_op_root_relpath);
4695   else
4696     move_op_depth = 0;
4697
4698   SVN_ERR(db_op_copy(ocb->src_wcroot, ocb->src_relpath,
4699                      ocb->dst_wcroot, ocb->dst_relpath,
4700                      ocb->work_items, move_op_depth, scratch_pool));
4701
4702   return SVN_NO_ERROR;
4703 }
4704
4705 svn_error_t *
4706 svn_wc__db_op_copy(svn_wc__db_t *db,
4707                    const char *src_abspath,
4708                    const char *dst_abspath,
4709                    const char *dst_op_root_abspath,
4710                    svn_boolean_t is_move,
4711                    const svn_skel_t *work_items,
4712                    apr_pool_t *scratch_pool)
4713 {
4714   struct op_copy_baton ocb = {0};
4715
4716   SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
4717   SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
4718   SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_op_root_abspath));
4719
4720   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
4721                                                 &ocb.src_relpath, db,
4722                                                 src_abspath,
4723                                                 scratch_pool, scratch_pool));
4724   VERIFY_USABLE_WCROOT(ocb.src_wcroot);
4725
4726   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
4727                                                 &ocb.dst_relpath,
4728                                                 db, dst_abspath,
4729                                                 scratch_pool, scratch_pool));
4730   VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
4731
4732   ocb.work_items = work_items;
4733   ocb.is_move = is_move;
4734   ocb.dst_op_root_relpath = svn_dirent_skip_ancestor(ocb.dst_wcroot->abspath,
4735                                                      dst_op_root_abspath);
4736
4737   /* Call with the sdb in src_wcroot. It might call itself again to
4738      also obtain a lock in dst_wcroot */
4739   SVN_ERR(svn_sqlite__with_lock(ocb.src_wcroot->sdb, op_copy_txn, &ocb,
4740                                 scratch_pool));
4741
4742   return SVN_NO_ERROR;
4743 }
4744
4745 /* The txn body of svn_wc__db_op_handle_move_back */
4746 static svn_error_t *
4747 handle_move_back(svn_boolean_t *moved_back,
4748                  svn_wc__db_wcroot_t *wcroot,
4749                  const char *local_relpath,
4750                  const char *moved_from_relpath,
4751                  const svn_skel_t *work_items,
4752                  apr_pool_t *scratch_pool)
4753 {
4754   svn_sqlite__stmt_t *stmt;
4755   svn_wc__db_status_t status;
4756   svn_boolean_t op_root;
4757   svn_boolean_t have_more_work;
4758   int from_op_depth = 0;
4759   svn_boolean_t have_row;
4760   svn_boolean_t different = FALSE;
4761
4762   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
4763
4764   SVN_ERR(svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL, NULL,
4765                                         NULL, NULL, NULL, NULL, NULL, NULL,
4766                                         NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4767                                         &op_root, NULL, NULL, NULL,
4768                                         &have_more_work, NULL,
4769                                         wcroot, local_relpath,
4770                                         scratch_pool, scratch_pool));
4771
4772   if (status != svn_wc__db_status_added || !op_root)
4773     return SVN_NO_ERROR;
4774
4775   /* We have two cases here: BASE-move-back and WORKING-move-back */
4776   if (have_more_work)
4777     SVN_ERR(op_depth_of(&from_op_depth, wcroot,
4778                         svn_relpath_dirname(local_relpath, scratch_pool)));
4779   else
4780     from_op_depth = 0;
4781
4782   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4783                                     STMT_SELECT_MOVED_BACK));
4784
4785   SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id,
4786                                           local_relpath,
4787                                           from_op_depth,
4788                                           relpath_depth(local_relpath)));
4789
4790   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4791
4792   SVN_ERR_ASSERT(have_row); /* We checked that the node is an op-root */
4793
4794   {
4795     svn_boolean_t moved_here = svn_sqlite__column_boolean(stmt, 9);
4796     const char *moved_to = svn_sqlite__column_text(stmt, 10, NULL);
4797
4798     if (!moved_here
4799         || !moved_to
4800         || strcmp(moved_to, moved_from_relpath))
4801       {
4802         different = TRUE;
4803         have_row = FALSE;
4804       }
4805   }
4806
4807   while (have_row)
4808     {
4809       svn_wc__db_status_t upper_status;
4810       svn_wc__db_status_t lower_status;
4811
4812       upper_status = svn_sqlite__column_token(stmt, 1, presence_map);
4813
4814       if (svn_sqlite__column_is_null(stmt, 5))
4815         {
4816           /* No lower layer replaced. */
4817           if (upper_status != svn_wc__db_status_not_present)
4818             {
4819               different = TRUE;
4820               break;
4821             }
4822           continue;
4823         }
4824
4825       lower_status = svn_sqlite__column_token(stmt, 5, presence_map);
4826
4827       if (upper_status != lower_status)
4828         {
4829           different = TRUE;
4830           break;
4831         }
4832
4833       if (upper_status == svn_wc__db_status_not_present
4834           || upper_status == svn_wc__db_status_excluded)
4835         {
4836           SVN_ERR(svn_sqlite__step(&have_row, stmt));
4837           continue; /* Nothing to check */
4838         }
4839       else if (upper_status != svn_wc__db_status_normal)
4840         {
4841           /* Not a normal move. Mixed revision move? */
4842           different = TRUE;
4843           break;
4844         }
4845
4846       {
4847         const char *upper_repos_relpath;
4848         const char *lower_repos_relpath;
4849
4850         upper_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL);
4851         lower_repos_relpath = svn_sqlite__column_text(stmt, 7, NULL);
4852
4853         if (! upper_repos_relpath
4854             || strcmp(upper_repos_relpath, lower_repos_relpath))
4855           {
4856             different = TRUE;
4857             break;
4858           }
4859       }
4860
4861       {
4862         svn_revnum_t upper_rev;
4863         svn_revnum_t lower_rev;
4864
4865         upper_rev = svn_sqlite__column_revnum(stmt, 4);
4866         lower_rev = svn_sqlite__column_revnum(stmt, 8);
4867
4868         if (upper_rev != lower_rev)
4869           {
4870             different = TRUE;
4871             break;
4872           }
4873       }
4874
4875       {
4876         apr_int64_t upper_repos_id;
4877         apr_int64_t lower_repos_id;
4878
4879         upper_repos_id = svn_sqlite__column_int64(stmt, 2);
4880         lower_repos_id = svn_sqlite__column_int64(stmt, 6);
4881
4882         if (upper_repos_id != lower_repos_id)
4883           {
4884             different = TRUE;
4885             break;
4886           }
4887       }
4888
4889       /* Check moved_here? */
4890
4891       SVN_ERR(svn_sqlite__step(&have_row, stmt));
4892     }
4893   SVN_ERR(svn_sqlite__reset(stmt));
4894
4895   if (! different)
4896     {
4897       /* Ok, we can now safely remove this complete move, because we
4898          determined that it 100% matches the layer below it. */
4899
4900       /* ### We could copy the recorded timestamps from the higher to the
4901              lower layer in an attempt to improve status performance, but
4902              generally these values should be the same anyway as it was
4903              a no-op move. */
4904       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4905                                         STMT_DELETE_MOVED_BACK));
4906
4907       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
4908                                              local_relpath,
4909                                              relpath_depth(local_relpath)));
4910
4911       SVN_ERR(svn_sqlite__step_done(stmt));
4912
4913       if (moved_back)
4914         *moved_back = TRUE;
4915     }
4916
4917   return SVN_NO_ERROR;
4918 }
4919
4920 svn_error_t *
4921 svn_wc__db_op_handle_move_back(svn_boolean_t *moved_back,
4922                                svn_wc__db_t *db,
4923                                const char *local_abspath,
4924                                const char *moved_from_abspath,
4925                                const svn_skel_t *work_items,
4926                                apr_pool_t *scratch_pool)
4927 {
4928   svn_wc__db_wcroot_t *wcroot;
4929   const char *local_relpath;
4930   const char *moved_from_relpath;
4931   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4932
4933   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4934                                                 local_abspath,
4935                                                 scratch_pool, scratch_pool));
4936   VERIFY_USABLE_WCROOT(wcroot);
4937
4938   if (moved_back)
4939     *moved_back = FALSE;
4940
4941   moved_from_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
4942                                                 moved_from_abspath);
4943
4944   if (! local_relpath[0]
4945       || !moved_from_relpath)
4946     {
4947        /* WC-Roots can't be moved */
4948       SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
4949       return SVN_NO_ERROR;
4950     }
4951
4952   SVN_WC__DB_WITH_TXN(handle_move_back(moved_back, wcroot, local_relpath,
4953                                        moved_from_relpath, work_items,
4954                                        scratch_pool),
4955                       wcroot);
4956
4957   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
4958                         scratch_pool));
4959
4960   return SVN_NO_ERROR;
4961 }
4962
4963
4964 /* The recursive implementation of svn_wc__db_op_copy_shadowed_layer.
4965  *
4966  * A non-zero MOVE_OP_DEPTH implies that the copy operation is part of
4967  * a move, and indicates the op-depth of the move destination op-root. */
4968 static svn_error_t *
4969 db_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot,
4970                           const char *src_relpath,
4971                           int src_op_depth,
4972                           svn_wc__db_wcroot_t *dst_wcroot,
4973                           const char *dst_relpath,
4974                           int dst_op_depth,
4975                           int del_op_depth,
4976                           apr_int64_t repos_id,
4977                           const char *repos_relpath,
4978                           svn_revnum_t revision,
4979                           int move_op_depth,
4980                           apr_pool_t *scratch_pool)
4981 {
4982   const apr_array_header_t *children;
4983   apr_pool_t *iterpool;
4984   svn_wc__db_status_t status;
4985   svn_node_kind_t kind;
4986   svn_revnum_t node_revision;
4987   const char *node_repos_relpath;
4988   apr_int64_t node_repos_id;
4989   svn_sqlite__stmt_t *stmt;
4990   svn_wc__db_status_t dst_presence;
4991   int i;
4992
4993   {
4994     svn_error_t *err;
4995     err = svn_wc__db_depth_get_info(&status, &kind, &node_revision,
4996                                     &node_repos_relpath, &node_repos_id,
4997                                     NULL, NULL, NULL, NULL, NULL, NULL,
4998                                     NULL, NULL,
4999                                     src_wcroot, src_relpath, src_op_depth,
5000                                     scratch_pool, scratch_pool);
5001
5002     if (err)
5003       {
5004         if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5005           return svn_error_trace(err);
5006
5007         svn_error_clear(err);
5008         return SVN_NO_ERROR; /* There is no shadowed node at src_op_depth */
5009       }
5010   }
5011
5012   if (src_op_depth == 0)
5013     {
5014       /* If the node is switched or has a different revision then its parent
5015          we shouldn't copy it. (We can't as we would have to insert it at
5016          an unshadowed depth) */
5017       if (status == svn_wc__db_status_not_present
5018           || status == svn_wc__db_status_excluded
5019           || status == svn_wc__db_status_server_excluded
5020           || node_revision != revision
5021           || node_repos_id != repos_id
5022           || strcmp(node_repos_relpath, repos_relpath))
5023         {
5024           /* Add a not-present node in the destination wcroot */
5025           struct insert_working_baton_t iwb;
5026           const char *repos_root_url;
5027           const char *repos_uuid;
5028
5029           SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
5030                                               src_wcroot->sdb, node_repos_id,
5031                                               scratch_pool));
5032
5033           SVN_ERR(create_repos_id(&node_repos_id, repos_root_url, repos_uuid,
5034                                   dst_wcroot->sdb, scratch_pool));
5035
5036           blank_iwb(&iwb);
5037
5038           iwb.op_depth = dst_op_depth;
5039           if (status != svn_wc__db_status_excluded)
5040             iwb.presence = svn_wc__db_status_not_present;
5041           else
5042             iwb.presence = svn_wc__db_status_excluded;
5043
5044           iwb.kind = kind;
5045
5046           iwb.original_repos_id = node_repos_id;
5047           iwb.original_revnum = node_revision;
5048           iwb.original_repos_relpath = node_repos_relpath;
5049
5050           SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5051                                       scratch_pool));
5052
5053           return SVN_NO_ERROR;
5054         }
5055     }
5056
5057   iterpool = svn_pool_create(scratch_pool);
5058
5059   switch (status)
5060     {
5061     case svn_wc__db_status_normal:
5062     case svn_wc__db_status_added:
5063     case svn_wc__db_status_moved_here:
5064     case svn_wc__db_status_copied:
5065       dst_presence = svn_wc__db_status_normal;
5066       break;
5067     case svn_wc__db_status_deleted:
5068     case svn_wc__db_status_not_present:
5069       dst_presence = svn_wc__db_status_not_present;
5070       break;
5071     case svn_wc__db_status_excluded:
5072       dst_presence = svn_wc__db_status_excluded;
5073       break;
5074     case svn_wc__db_status_server_excluded:
5075       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5076                                _("Cannot copy '%s' excluded by server"),
5077                                path_for_error_message(src_wcroot,
5078                                                       src_relpath,
5079                                                       scratch_pool));
5080     default:
5081       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5082                                _("Cannot handle status of '%s'"),
5083                                path_for_error_message(src_wcroot,
5084                                                       src_relpath,
5085                                                       scratch_pool));
5086     }
5087
5088   if (dst_presence == svn_wc__db_status_normal
5089       && src_wcroot == dst_wcroot) /* ### Remove limitation */
5090     {
5091       SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
5092                              STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH));
5093
5094       SVN_ERR(svn_sqlite__bindf(stmt, "issdstd",
5095                         src_wcroot->wc_id, src_relpath,
5096                         dst_relpath,
5097                         dst_op_depth,
5098                         svn_relpath_dirname(dst_relpath, iterpool),
5099                         presence_map, dst_presence,
5100                         src_op_depth));
5101
5102       /* moved_here */
5103       if (dst_op_depth == move_op_depth)
5104         SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
5105
5106       SVN_ERR(svn_sqlite__step_done(stmt));
5107
5108       {
5109         /* And mark it deleted to allow proper shadowing */
5110         struct insert_working_baton_t iwb;
5111
5112         blank_iwb(&iwb);
5113
5114         iwb.op_depth = del_op_depth;
5115         iwb.presence = svn_wc__db_status_base_deleted;
5116
5117         iwb.kind = kind;
5118
5119         SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5120                                     scratch_pool));
5121       }
5122     }
5123   else
5124     {
5125       struct insert_working_baton_t iwb;
5126       if (dst_presence == svn_wc__db_status_normal) /* Fallback for multi-db */
5127         dst_presence = svn_wc__db_status_not_present;
5128
5129       /* And mark it deleted to allow proper shadowing */
5130
5131       blank_iwb(&iwb);
5132
5133       iwb.op_depth = dst_op_depth;
5134       iwb.presence = dst_presence;
5135       iwb.kind = kind;
5136
5137       SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5138                                     scratch_pool));
5139     }
5140
5141   if (dst_presence == svn_wc__db_status_not_present)
5142     {
5143       /* Don't create descendants of a not present node! */
5144
5145       /* This code is currently still triggered by copying deleted nodes
5146          between separate working copies. See ### comment above. */
5147
5148       svn_pool_destroy(iterpool);
5149       return SVN_NO_ERROR;
5150     }
5151
5152   SVN_ERR(gather_repo_children(&children, src_wcroot, src_relpath,
5153                                src_op_depth, scratch_pool, iterpool));
5154
5155   for (i = 0; i < children->nelts; i++)
5156     {
5157       const char *name = APR_ARRAY_IDX(children, i, const char *);
5158       const char *child_src_relpath;
5159       const char *child_dst_relpath;
5160       const char *child_repos_relpath = NULL;
5161
5162       svn_pool_clear(iterpool);
5163       child_src_relpath = svn_relpath_join(src_relpath, name, iterpool);
5164       child_dst_relpath = svn_relpath_join(dst_relpath, name, iterpool);
5165
5166       if (repos_relpath)
5167         child_repos_relpath = svn_relpath_join(repos_relpath, name, iterpool);
5168
5169       SVN_ERR(db_op_copy_shadowed_layer(
5170                          src_wcroot, child_src_relpath, src_op_depth,
5171                          dst_wcroot, child_dst_relpath, dst_op_depth,
5172                          del_op_depth,
5173                          repos_id, child_repos_relpath, revision,
5174                          move_op_depth, scratch_pool));
5175     }
5176
5177   svn_pool_destroy(iterpool);
5178
5179   return SVN_NO_ERROR;
5180 }
5181
5182 /* Helper for svn_wc__db_op_copy_shadowed_layer().
5183  *
5184  * Implements  svn_sqlite__transaction_callback_t. */
5185 static svn_error_t *
5186 op_copy_shadowed_layer_txn(void *baton,
5187                            svn_sqlite__db_t *sdb,
5188                            apr_pool_t *scratch_pool)
5189 {
5190   struct op_copy_baton *ocb = baton;
5191   const char *src_parent_relpath;
5192   const char *dst_parent_relpath;
5193   int src_op_depth;
5194   int dst_op_depth;
5195   int del_op_depth;
5196   const char *repos_relpath = NULL;
5197   apr_int64_t repos_id = INVALID_REPOS_ID;
5198   svn_revnum_t revision = SVN_INVALID_REVNUM;
5199
5200   if (sdb != ocb->dst_wcroot->sdb)
5201     {
5202        /* Source and destination databases differ; so also start a lock
5203           in the destination database, by calling ourself in a lock. */
5204
5205       return svn_error_trace(
5206                         svn_sqlite__with_lock(ocb->dst_wcroot->sdb,
5207                                               op_copy_shadowed_layer_txn,
5208                                               ocb, scratch_pool));
5209     }
5210
5211   /* From this point we can assume a lock in the src and dst databases */
5212
5213
5214   /* src_relpath and dst_relpath can't be wcroot as we need their parents */
5215   SVN_ERR_ASSERT(*ocb->src_relpath && *ocb->dst_relpath);
5216
5217   src_parent_relpath = svn_relpath_dirname(ocb->src_relpath, scratch_pool);
5218   dst_parent_relpath = svn_relpath_dirname(ocb->dst_relpath, scratch_pool);
5219
5220   /* src_parent must be status normal or added; get its op-depth */
5221   SVN_ERR(op_depth_of(&src_op_depth, ocb->src_wcroot, src_parent_relpath));
5222
5223   /* dst_parent must be status added; get its op-depth */
5224   SVN_ERR(op_depth_of(&dst_op_depth, ocb->dst_wcroot, dst_parent_relpath));
5225
5226   del_op_depth = relpath_depth(ocb->dst_relpath);
5227
5228   /* Get some information from the parent */
5229   SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, &revision, &repos_relpath,
5230                                     &repos_id, NULL, NULL, NULL, NULL, NULL,
5231                                     NULL, NULL, NULL,
5232                                     ocb->src_wcroot,
5233                                     src_parent_relpath, src_op_depth,
5234                                     scratch_pool, scratch_pool));
5235
5236   if (repos_relpath == NULL)
5237     {
5238       /* The node is a local addition and has no shadowed information */
5239       return SVN_NO_ERROR;
5240     }
5241
5242   /* And calculate the child repos relpath */
5243   repos_relpath = svn_relpath_join(repos_relpath,
5244                                    svn_relpath_basename(ocb->src_relpath,
5245                                                         NULL),
5246                                    scratch_pool);
5247
5248   SVN_ERR(db_op_copy_shadowed_layer(
5249                         ocb->src_wcroot, ocb->src_relpath, src_op_depth,
5250                         ocb->dst_wcroot, ocb->dst_relpath, dst_op_depth,
5251                         del_op_depth,
5252                         repos_id, repos_relpath, revision,
5253                         (ocb->is_move ? dst_op_depth : 0),
5254                         scratch_pool));
5255
5256   return SVN_NO_ERROR;
5257 }
5258
5259 svn_error_t *
5260 svn_wc__db_op_copy_shadowed_layer(svn_wc__db_t *db,
5261                                   const char *src_abspath,
5262                                   const char *dst_abspath,
5263                                   svn_boolean_t is_move,
5264                                   apr_pool_t *scratch_pool)
5265 {
5266   struct op_copy_baton ocb = {0};
5267
5268   SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
5269   SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
5270
5271   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
5272                                                 &ocb.src_relpath, db,
5273                                                 src_abspath,
5274                                                 scratch_pool, scratch_pool));
5275   VERIFY_USABLE_WCROOT(ocb.src_wcroot);
5276
5277   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
5278                                                 &ocb.dst_relpath,
5279                                                 db, dst_abspath,
5280                                                 scratch_pool, scratch_pool));
5281   VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
5282
5283   ocb.is_move = is_move;
5284   ocb.dst_op_root_relpath = NULL; /* not used by op_copy_shadowed_layer_txn */
5285
5286   ocb.work_items = NULL;
5287
5288   /* Call with the sdb in src_wcroot. It might call itself again to
5289      also obtain a lock in dst_wcroot */
5290   SVN_ERR(svn_sqlite__with_lock(ocb.src_wcroot->sdb,
5291                                 op_copy_shadowed_layer_txn,
5292                                 &ocb, scratch_pool));
5293
5294   return SVN_NO_ERROR;
5295 }
5296
5297
5298 /* If there are any server-excluded base nodes then the copy must fail
5299    as it's not possible to commit such a copy.
5300    Return an error if there are any server-excluded nodes. */
5301 static svn_error_t *
5302 catch_copy_of_server_excluded(svn_wc__db_wcroot_t *wcroot,
5303                               const char *local_relpath,
5304                               apr_pool_t *scratch_pool)
5305 {
5306   svn_sqlite__stmt_t *stmt;
5307   svn_boolean_t have_row;
5308   const char *server_excluded_relpath;
5309
5310   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5311                                     STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
5312   SVN_ERR(svn_sqlite__bindf(stmt, "is",
5313                             wcroot->wc_id,
5314                             local_relpath));
5315   SVN_ERR(svn_sqlite__step(&have_row, stmt));
5316   if (have_row)
5317     server_excluded_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
5318   SVN_ERR(svn_sqlite__reset(stmt));
5319   if (have_row)
5320     return svn_error_createf(SVN_ERR_AUTHZ_UNREADABLE, NULL,
5321                              _("Cannot copy '%s' excluded by server"),
5322                              path_for_error_message(wcroot,
5323                                                     server_excluded_relpath,
5324                                                     scratch_pool));
5325
5326   return SVN_NO_ERROR;
5327 }
5328
5329
5330 svn_error_t *
5331 svn_wc__db_op_copy_dir(svn_wc__db_t *db,
5332                        const char *local_abspath,
5333                        const apr_hash_t *props,
5334                        svn_revnum_t changed_rev,
5335                        apr_time_t changed_date,
5336                        const char *changed_author,
5337                        const char *original_repos_relpath,
5338                        const char *original_root_url,
5339                        const char *original_uuid,
5340                        svn_revnum_t original_revision,
5341                        const apr_array_header_t *children,
5342                        svn_boolean_t is_move,
5343                        svn_depth_t depth,
5344                        const svn_skel_t *conflict,
5345                        const svn_skel_t *work_items,
5346                        apr_pool_t *scratch_pool)
5347 {
5348   svn_wc__db_wcroot_t *wcroot;
5349   const char *local_relpath;
5350   insert_working_baton_t iwb;
5351   int parent_op_depth;
5352
5353   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5354   SVN_ERR_ASSERT(props != NULL);
5355   /* ### any assertions for CHANGED_* ?  */
5356   /* ### any assertions for ORIGINAL_* ?  */
5357 #if 0
5358   SVN_ERR_ASSERT(children != NULL);
5359 #endif
5360
5361   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5362                               local_abspath, scratch_pool, scratch_pool));
5363   VERIFY_USABLE_WCROOT(wcroot);
5364
5365   blank_iwb(&iwb);
5366
5367   iwb.presence = svn_wc__db_status_normal;
5368   iwb.kind = svn_node_dir;
5369
5370   iwb.props = props;
5371   iwb.changed_rev = changed_rev;
5372   iwb.changed_date = changed_date;
5373   iwb.changed_author = changed_author;
5374
5375   if (original_root_url != NULL)
5376     {
5377       SVN_ERR(create_repos_id(&iwb.original_repos_id,
5378                               original_root_url, original_uuid,
5379                               wcroot->sdb, scratch_pool));
5380       iwb.original_repos_relpath = original_repos_relpath;
5381       iwb.original_revnum = original_revision;
5382     }
5383
5384   /* ### Should we do this inside the transaction? */
5385   SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5386                             &parent_op_depth, iwb.original_repos_id,
5387                             original_repos_relpath, original_revision,
5388                             wcroot, local_relpath, scratch_pool));
5389
5390   iwb.children = children;
5391   iwb.depth = depth;
5392   iwb.moved_here = is_move && (parent_op_depth == 0 ||
5393                                iwb.op_depth == parent_op_depth);
5394
5395   iwb.work_items = work_items;
5396   iwb.conflict = conflict;
5397
5398   SVN_WC__DB_WITH_TXN(
5399                 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5400                 wcroot);
5401   SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
5402
5403   return SVN_NO_ERROR;
5404 }
5405
5406
5407 svn_error_t *
5408 svn_wc__db_op_copy_file(svn_wc__db_t *db,
5409                         const char *local_abspath,
5410                         const apr_hash_t *props,
5411                         svn_revnum_t changed_rev,
5412                         apr_time_t changed_date,
5413                         const char *changed_author,
5414                         const char *original_repos_relpath,
5415                         const char *original_root_url,
5416                         const char *original_uuid,
5417                         svn_revnum_t original_revision,
5418                         const svn_checksum_t *checksum,
5419                         svn_boolean_t update_actual_props,
5420                         const apr_hash_t *new_actual_props,
5421                         svn_boolean_t is_move,
5422                         const svn_skel_t *conflict,
5423                         const svn_skel_t *work_items,
5424                         apr_pool_t *scratch_pool)
5425 {
5426   svn_wc__db_wcroot_t *wcroot;
5427   const char *local_relpath;
5428   insert_working_baton_t iwb;
5429   int parent_op_depth;
5430
5431   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5432   SVN_ERR_ASSERT(props != NULL);
5433   /* ### any assertions for CHANGED_* ?  */
5434   SVN_ERR_ASSERT((! original_repos_relpath && ! original_root_url
5435                   && ! original_uuid && ! checksum
5436                   && original_revision == SVN_INVALID_REVNUM)
5437                  || (original_repos_relpath && original_root_url
5438                      && original_uuid && checksum
5439                      && original_revision != SVN_INVALID_REVNUM));
5440
5441   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5442                               local_abspath, scratch_pool, scratch_pool));
5443   VERIFY_USABLE_WCROOT(wcroot);
5444
5445   blank_iwb(&iwb);
5446
5447   iwb.presence = svn_wc__db_status_normal;
5448   iwb.kind = svn_node_file;
5449
5450   iwb.props = props;
5451   iwb.changed_rev = changed_rev;
5452   iwb.changed_date = changed_date;
5453   iwb.changed_author = changed_author;
5454
5455   if (original_root_url != NULL)
5456     {
5457       SVN_ERR(create_repos_id(&iwb.original_repos_id,
5458                               original_root_url, original_uuid,
5459                               wcroot->sdb, scratch_pool));
5460       iwb.original_repos_relpath = original_repos_relpath;
5461       iwb.original_revnum = original_revision;
5462     }
5463
5464   /* ### Should we do this inside the transaction? */
5465   SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5466                             &parent_op_depth, iwb.original_repos_id,
5467                             original_repos_relpath, original_revision,
5468                             wcroot, local_relpath, scratch_pool));
5469
5470   iwb.checksum = checksum;
5471   iwb.moved_here = is_move && (parent_op_depth == 0 ||
5472                                iwb.op_depth == parent_op_depth);
5473
5474   if (update_actual_props)
5475     {
5476       iwb.update_actual_props = update_actual_props;
5477       iwb.new_actual_props = new_actual_props;
5478     }
5479
5480   iwb.work_items = work_items;
5481   iwb.conflict = conflict;
5482
5483   SVN_WC__DB_WITH_TXN(
5484           insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5485           wcroot);
5486   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5487
5488   return SVN_NO_ERROR;
5489 }
5490
5491
5492 svn_error_t *
5493 svn_wc__db_op_copy_symlink(svn_wc__db_t *db,
5494                            const char *local_abspath,
5495                            const apr_hash_t *props,
5496                            svn_revnum_t changed_rev,
5497                            apr_time_t changed_date,
5498                            const char *changed_author,
5499                            const char *original_repos_relpath,
5500                            const char *original_root_url,
5501                            const char *original_uuid,
5502                            svn_revnum_t original_revision,
5503                            const char *target,
5504                            const svn_skel_t *conflict,
5505                            const svn_skel_t *work_items,
5506                            apr_pool_t *scratch_pool)
5507 {
5508   svn_wc__db_wcroot_t *wcroot;
5509   const char *local_relpath;
5510   insert_working_baton_t iwb;
5511   int parent_op_depth;
5512
5513   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5514   SVN_ERR_ASSERT(props != NULL);
5515   /* ### any assertions for CHANGED_* ?  */
5516   /* ### any assertions for ORIGINAL_* ?  */
5517   SVN_ERR_ASSERT(target != NULL);
5518
5519   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5520                               local_abspath, scratch_pool, scratch_pool));
5521   VERIFY_USABLE_WCROOT(wcroot);
5522
5523   blank_iwb(&iwb);
5524
5525   iwb.presence = svn_wc__db_status_normal;
5526   iwb.kind = svn_node_symlink;
5527
5528   iwb.props = props;
5529   iwb.changed_rev = changed_rev;
5530   iwb.changed_date = changed_date;
5531   iwb.changed_author = changed_author;
5532   iwb.moved_here = FALSE;
5533
5534   if (original_root_url != NULL)
5535     {
5536       SVN_ERR(create_repos_id(&iwb.original_repos_id,
5537                               original_root_url, original_uuid,
5538                               wcroot->sdb, scratch_pool));
5539       iwb.original_repos_relpath = original_repos_relpath;
5540       iwb.original_revnum = original_revision;
5541     }
5542
5543   /* ### Should we do this inside the transaction? */
5544   SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5545                             &parent_op_depth, iwb.original_repos_id,
5546                             original_repos_relpath, original_revision,
5547                             wcroot, local_relpath, scratch_pool));
5548
5549   iwb.target = target;
5550
5551   iwb.work_items = work_items;
5552   iwb.conflict = conflict;
5553
5554   SVN_WC__DB_WITH_TXN(
5555             insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5556             wcroot);
5557   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5558
5559   return SVN_NO_ERROR;
5560 }
5561
5562
5563 svn_error_t *
5564 svn_wc__db_op_add_directory(svn_wc__db_t *db,
5565                             const char *local_abspath,
5566                             const apr_hash_t *props,
5567                             const svn_skel_t *work_items,
5568                             apr_pool_t *scratch_pool)
5569 {
5570   svn_wc__db_wcroot_t *wcroot;
5571   const char *local_relpath;
5572   const char *dir_abspath;
5573   const char *name;
5574   insert_working_baton_t iwb;
5575
5576   /* Resolve wcroot via parent directory to avoid obstruction handling */
5577   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5578   svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5579
5580   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5581                               dir_abspath, scratch_pool, scratch_pool));
5582   VERIFY_USABLE_WCROOT(wcroot);
5583
5584   blank_iwb(&iwb);
5585
5586   local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5587   iwb.presence = svn_wc__db_status_normal;
5588   iwb.kind = svn_node_dir;
5589   iwb.op_depth = relpath_depth(local_relpath);
5590   if (props && apr_hash_count((apr_hash_t *)props))
5591     {
5592       iwb.update_actual_props = TRUE;
5593       iwb.new_actual_props = props;
5594     }
5595
5596   iwb.work_items = work_items;
5597
5598   SVN_WC__DB_WITH_TXN(
5599             insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5600             wcroot);
5601   /* Use depth infinity to make sure we have no invalid cached information
5602    * about children of this dir. */
5603   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5604                         scratch_pool));
5605
5606   return SVN_NO_ERROR;
5607 }
5608
5609
5610 svn_error_t *
5611 svn_wc__db_op_add_file(svn_wc__db_t *db,
5612                        const char *local_abspath,
5613                        const apr_hash_t *props,
5614                        const svn_skel_t *work_items,
5615                        apr_pool_t *scratch_pool)
5616 {
5617   svn_wc__db_wcroot_t *wcroot;
5618   const char *local_relpath;
5619   insert_working_baton_t iwb;
5620   const char *dir_abspath;
5621   const char *name;
5622
5623   /* Resolve wcroot via parent directory to avoid obstruction handling */
5624   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5625   svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5626
5627   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5628                               dir_abspath, scratch_pool, scratch_pool));
5629   VERIFY_USABLE_WCROOT(wcroot);
5630
5631   blank_iwb(&iwb);
5632
5633   local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5634   iwb.presence = svn_wc__db_status_normal;
5635   iwb.kind = svn_node_file;
5636   iwb.op_depth = relpath_depth(local_relpath);
5637   if (props && apr_hash_count((apr_hash_t *)props))
5638     {
5639       iwb.update_actual_props = TRUE;
5640       iwb.new_actual_props = props;
5641     }
5642
5643   iwb.work_items = work_items;
5644
5645   SVN_WC__DB_WITH_TXN(
5646             insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5647             wcroot);
5648   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5649
5650   return SVN_NO_ERROR;
5651 }
5652
5653
5654 svn_error_t *
5655 svn_wc__db_op_add_symlink(svn_wc__db_t *db,
5656                           const char *local_abspath,
5657                           const char *target,
5658                           const apr_hash_t *props,
5659                           const svn_skel_t *work_items,
5660                           apr_pool_t *scratch_pool)
5661 {
5662   svn_wc__db_wcroot_t *wcroot;
5663   const char *local_relpath;
5664   insert_working_baton_t iwb;
5665   const char *dir_abspath;
5666   const char *name;
5667
5668   /* Resolve wcroot via parent directory to avoid obstruction handling */
5669   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5670   SVN_ERR_ASSERT(target != NULL);
5671
5672   svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5673
5674   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5675                               dir_abspath, scratch_pool, scratch_pool));
5676
5677   VERIFY_USABLE_WCROOT(wcroot);
5678
5679   blank_iwb(&iwb);
5680
5681   local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5682   iwb.presence = svn_wc__db_status_normal;
5683   iwb.kind = svn_node_symlink;
5684   iwb.op_depth = relpath_depth(local_relpath);
5685   if (props && apr_hash_count((apr_hash_t *)props))
5686     {
5687       iwb.update_actual_props = TRUE;
5688       iwb.new_actual_props = props;
5689     }
5690
5691   iwb.target = target;
5692
5693   iwb.work_items = work_items;
5694
5695   SVN_WC__DB_WITH_TXN(
5696             insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5697             wcroot);
5698   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5699
5700   return SVN_NO_ERROR;
5701 }
5702
5703 /* Record RECORDED_SIZE and RECORDED_TIME into top layer in NODES */
5704 static svn_error_t *
5705 db_record_fileinfo(svn_wc__db_wcroot_t *wcroot,
5706                    const char *local_relpath,
5707                    apr_int64_t recorded_size,
5708                    apr_int64_t recorded_time,
5709                    apr_pool_t *scratch_pool)
5710 {
5711   svn_sqlite__stmt_t *stmt;
5712   int affected_rows;
5713
5714   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5715                                     STMT_UPDATE_NODE_FILEINFO));
5716   SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
5717                             recorded_size, recorded_time));
5718   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
5719
5720   SVN_ERR_ASSERT(affected_rows == 1);
5721
5722   return SVN_NO_ERROR;
5723 }
5724
5725
5726 svn_error_t *
5727 svn_wc__db_global_record_fileinfo(svn_wc__db_t *db,
5728                                   const char *local_abspath,
5729                                   svn_filesize_t recorded_size,
5730                                   apr_time_t recorded_time,
5731                                   apr_pool_t *scratch_pool)
5732 {
5733   svn_wc__db_wcroot_t *wcroot;
5734   const char *local_relpath;
5735
5736   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5737
5738   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5739                               local_abspath, scratch_pool, scratch_pool));
5740   VERIFY_USABLE_WCROOT(wcroot);
5741
5742   SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
5743                              recorded_size, recorded_time, scratch_pool));
5744
5745   /* We *totally* monkeyed the entries. Toss 'em.  */
5746   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5747
5748   return SVN_NO_ERROR;
5749 }
5750
5751
5752 /* Set the ACTUAL_NODE properties column for (WC_ID, LOCAL_RELPATH) to
5753  * PROPS.
5754  *
5755  * Note: PROPS=NULL means the actual props are the same as the pristine
5756  * props; to indicate no properties when the pristine has some props,
5757  * PROPS must be an empty hash. */
5758 static svn_error_t *
5759 set_actual_props(apr_int64_t wc_id,
5760                  const char *local_relpath,
5761                  apr_hash_t *props,
5762                  svn_sqlite__db_t *db,
5763                  apr_pool_t *scratch_pool)
5764 {
5765   svn_sqlite__stmt_t *stmt;
5766   int affected_rows;
5767
5768   SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_UPDATE_ACTUAL_PROPS));
5769   SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
5770   SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
5771   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
5772
5773   if (affected_rows == 1 || !props)
5774     return SVN_NO_ERROR; /* We are done */
5775
5776   /* We have to insert a row in ACTUAL */
5777
5778   SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_ACTUAL_PROPS));
5779   SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
5780   if (*local_relpath != '\0')
5781     SVN_ERR(svn_sqlite__bind_text(stmt, 3,
5782                                   svn_relpath_dirname(local_relpath,
5783                                                       scratch_pool)));
5784   SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
5785   return svn_error_trace(svn_sqlite__step_done(stmt));
5786 }
5787
5788
5789 /* The body of svn_wc__db_op_set_props().
5790
5791    Set the 'properties' column in the 'ACTUAL_NODE' table to BATON->props.
5792    Create an entry in the ACTUAL table for the node if it does not yet
5793    have one.
5794    To specify no properties, BATON->props must be an empty hash, not NULL.
5795    BATON is of type 'struct set_props_baton_t'.
5796 */
5797 static svn_error_t *
5798 set_props_txn(svn_wc__db_wcroot_t *wcroot,
5799               const char *local_relpath,
5800               apr_hash_t *props,
5801               svn_boolean_t clear_recorded_info,
5802               const svn_skel_t *conflict,
5803               const svn_skel_t *work_items,
5804               apr_pool_t *scratch_pool)
5805 {
5806   apr_hash_t *pristine_props;
5807
5808   /* Check if the props are modified. If no changes, then wipe out the
5809      ACTUAL props.  PRISTINE_PROPS==NULL means that any
5810      ACTUAL props are okay as provided, so go ahead and set them.  */
5811   SVN_ERR(db_read_pristine_props(&pristine_props, wcroot, local_relpath, FALSE,
5812                                  scratch_pool, scratch_pool));
5813   if (props && pristine_props)
5814     {
5815       apr_array_header_t *prop_diffs;
5816
5817       SVN_ERR(svn_prop_diffs(&prop_diffs, props, pristine_props,
5818                              scratch_pool));
5819       if (prop_diffs->nelts == 0)
5820         props = NULL;
5821     }
5822
5823   SVN_ERR(set_actual_props(wcroot->wc_id, local_relpath,
5824                            props, wcroot->sdb, scratch_pool));
5825
5826   if (clear_recorded_info)
5827     {
5828       SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
5829                                  SVN_INVALID_FILESIZE, 0,
5830                                  scratch_pool));
5831     }
5832
5833   /* And finally.  */
5834   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5835   if (conflict)
5836     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
5837                                               conflict, scratch_pool));
5838
5839   return SVN_NO_ERROR;
5840 }
5841
5842
5843 svn_error_t *
5844 svn_wc__db_op_set_props(svn_wc__db_t *db,
5845                         const char *local_abspath,
5846                         apr_hash_t *props,
5847                         svn_boolean_t clear_recorded_info,
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
5855   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5856
5857   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
5858                               db, local_abspath, scratch_pool, scratch_pool));
5859   VERIFY_USABLE_WCROOT(wcroot);
5860
5861   SVN_WC__DB_WITH_TXN(set_props_txn(wcroot, local_relpath, props,
5862                                     clear_recorded_info, conflict, work_items,
5863                                     scratch_pool),
5864                       wcroot);
5865   return SVN_NO_ERROR;
5866 }
5867
5868
5869 svn_error_t *
5870 svn_wc__db_op_modified(svn_wc__db_t *db,
5871                        const char *local_abspath,
5872                        apr_pool_t *scratch_pool)
5873 {
5874   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5875
5876   NOT_IMPLEMENTED();
5877 }
5878
5879 /* */
5880 static svn_error_t *
5881 populate_targets_tree(svn_wc__db_wcroot_t *wcroot,
5882                       const char *local_relpath,
5883                       svn_depth_t depth,
5884                       const apr_array_header_t *changelist_filter,
5885                       apr_pool_t *scratch_pool)
5886 {
5887   svn_sqlite__stmt_t *stmt;
5888   int affected_rows = 0;
5889   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
5890                                       STMT_CREATE_TARGETS_LIST));
5891
5892   if (changelist_filter && changelist_filter->nelts > 0)
5893     {
5894       /* Iterate over the changelists, adding the nodes which match.
5895          Common case: we only have one changelist, so this only
5896          happens once. */
5897       int i;
5898       int stmt_idx;
5899
5900       switch (depth)
5901         {
5902           case svn_depth_empty:
5903             stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST;
5904             break;
5905
5906           case svn_depth_files:
5907             stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES;
5908             break;
5909
5910           case svn_depth_immediates:
5911             stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES;
5912             break;
5913
5914           case svn_depth_infinity:
5915             stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY;
5916             break;
5917
5918           default:
5919             /* We don't know how to handle unknown or exclude. */
5920             SVN_ERR_MALFUNCTION();
5921             break;
5922         }
5923
5924       for (i = 0; i < changelist_filter->nelts; i++)
5925         {
5926           int sub_affected;
5927           const char *changelist = APR_ARRAY_IDX(changelist_filter, i,
5928                                                  const char *);
5929
5930           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5931                                         STMT_INSERT_TARGET_WITH_CHANGELIST));
5932           SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
5933                                     local_relpath, changelist));
5934           SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5935
5936           /* If the root is matched by the changelist, we don't have to match
5937              the children. As that tells us the root is a file */
5938           if (!sub_affected && depth > svn_depth_empty)
5939             {
5940               SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
5941               SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
5942                                         local_relpath, changelist));
5943               SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5944             }
5945
5946           affected_rows += sub_affected;
5947         }
5948     }
5949   else /* No changelist filtering */
5950     {
5951       int stmt_idx;
5952       int sub_affected;
5953
5954       switch (depth)
5955         {
5956           case svn_depth_empty:
5957             stmt_idx = STMT_INSERT_TARGET;
5958             break;
5959
5960           case svn_depth_files:
5961             stmt_idx = STMT_INSERT_TARGET_DEPTH_FILES;
5962             break;
5963
5964           case svn_depth_immediates:
5965             stmt_idx = STMT_INSERT_TARGET_DEPTH_IMMEDIATES;
5966             break;
5967
5968           case svn_depth_infinity:
5969             stmt_idx = STMT_INSERT_TARGET_DEPTH_INFINITY;
5970             break;
5971
5972           default:
5973             /* We don't know how to handle unknown or exclude. */
5974             SVN_ERR_MALFUNCTION();
5975             break;
5976         }
5977
5978       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5979                                         STMT_INSERT_TARGET));
5980       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
5981       SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5982       affected_rows += sub_affected;
5983
5984       if (depth > svn_depth_empty)
5985         {
5986           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
5987           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
5988           SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
5989           affected_rows += sub_affected;
5990         }
5991     }
5992
5993   /* Does the target exist? */
5994   if (affected_rows == 0)
5995     {
5996       svn_boolean_t exists;
5997       SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
5998
5999       if (!exists)
6000         return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6001                                  _("The node '%s' was not found."),
6002                                  path_for_error_message(wcroot,
6003                                                         local_relpath,
6004                                                         scratch_pool));
6005     }
6006
6007   return SVN_NO_ERROR;
6008 }
6009
6010
6011 #if 0
6012 static svn_error_t *
6013 dump_targets(svn_wc__db_wcroot_t *wcroot,
6014              apr_pool_t *scratch_pool)
6015 {
6016   svn_sqlite__stmt_t *stmt;
6017   svn_boolean_t have_row;
6018
6019   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6020                                     STMT_SELECT_TARGETS));
6021   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6022   while (have_row)
6023     {
6024       const char *target = svn_sqlite__column_text(stmt, 0, NULL);
6025       SVN_DBG(("Target: '%s'\n", target));
6026       SVN_ERR(svn_sqlite__step(&have_row, stmt));
6027     }
6028
6029   SVN_ERR(svn_sqlite__reset(stmt));
6030
6031   return SVN_NO_ERROR;
6032 }
6033 #endif
6034
6035
6036 struct set_changelist_baton_t
6037 {
6038   const char *new_changelist;
6039   const apr_array_header_t *changelist_filter;
6040   svn_depth_t depth;
6041 };
6042
6043
6044 /* The main part of svn_wc__db_op_set_changelist().
6045  *
6046  * Implements svn_wc__db_txn_callback_t. */
6047 static svn_error_t *
6048 set_changelist_txn(void *baton,
6049                    svn_wc__db_wcroot_t *wcroot,
6050                    const char *local_relpath,
6051                    apr_pool_t *scratch_pool)
6052 {
6053   struct set_changelist_baton_t *scb = baton;
6054   svn_sqlite__stmt_t *stmt;
6055
6056   SVN_ERR(populate_targets_tree(wcroot, local_relpath, scb->depth,
6057                                 scb->changelist_filter, scratch_pool));
6058
6059   /* Ensure we have actual nodes for our targets. */
6060   if (scb->new_changelist)
6061     {
6062       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6063                                         STMT_INSERT_ACTUAL_EMPTIES));
6064       SVN_ERR(svn_sqlite__step_done(stmt));
6065     }
6066
6067   /* Now create our notification table. */
6068   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6069                                       STMT_CREATE_CHANGELIST_LIST));
6070   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6071                                       STMT_CREATE_CHANGELIST_TRIGGER));
6072
6073   /* Update our changelists. */
6074   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6075                                     STMT_UPDATE_ACTUAL_CHANGELISTS));
6076   SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6077                             scb->new_changelist));
6078   SVN_ERR(svn_sqlite__step_done(stmt));
6079
6080   if (scb->new_changelist)
6081     {
6082       /* We have to notify that we skipped directories, so do that now. */
6083       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6084                                         STMT_MARK_SKIPPED_CHANGELIST_DIRS));
6085       SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6086                                 scb->new_changelist));
6087       SVN_ERR(svn_sqlite__step_done(stmt));
6088     }
6089
6090   /* We may have left empty ACTUAL nodes, so remove them.  This is only a
6091      potential problem if we removed changelists. */
6092   if (!scb->new_changelist)
6093     {
6094       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6095                                         STMT_DELETE_ACTUAL_EMPTIES));
6096       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6097       SVN_ERR(svn_sqlite__step_done(stmt));
6098     }
6099
6100   return SVN_NO_ERROR;
6101 }
6102
6103
6104 /* Send notifications for svn_wc__db_op_set_changelist().
6105  *
6106  * Implements work_callback_t. */
6107 static svn_error_t *
6108 do_changelist_notify(void *baton,
6109                      svn_wc__db_wcroot_t *wcroot,
6110                      svn_cancel_func_t cancel_func,
6111                      void *cancel_baton,
6112                      svn_wc_notify_func2_t notify_func,
6113                      void *notify_baton,
6114                      apr_pool_t *scratch_pool)
6115 {
6116   svn_sqlite__stmt_t *stmt;
6117   svn_boolean_t have_row;
6118   apr_pool_t *iterpool;
6119
6120   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6121                                     STMT_SELECT_CHANGELIST_LIST));
6122   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6123
6124   iterpool = svn_pool_create(scratch_pool);
6125   while (have_row)
6126     {
6127       /* ### wc_id is column 0. use it one day...  */
6128       const char *notify_relpath = svn_sqlite__column_text(stmt, 1, NULL);
6129       svn_wc_notify_action_t action = svn_sqlite__column_int(stmt, 2);
6130       svn_wc_notify_t *notify;
6131       const char *notify_abspath;
6132
6133       svn_pool_clear(iterpool);
6134
6135       if (cancel_func)
6136         {
6137           svn_error_t *err = cancel_func(cancel_baton);
6138
6139           if (err)
6140             return svn_error_trace(svn_error_compose_create(
6141                                                     err,
6142                                                     svn_sqlite__reset(stmt)));
6143         }
6144
6145       notify_abspath = svn_dirent_join(wcroot->abspath, notify_relpath,
6146                                        iterpool);
6147       notify = svn_wc_create_notify(notify_abspath, action, iterpool);
6148       notify->changelist_name = svn_sqlite__column_text(stmt, 3, NULL);
6149       notify_func(notify_baton, notify, iterpool);
6150
6151       SVN_ERR(svn_sqlite__step(&have_row, stmt));
6152     }
6153   svn_pool_destroy(iterpool);
6154
6155   return svn_error_trace(svn_sqlite__reset(stmt));
6156 }
6157
6158
6159 svn_error_t *
6160 svn_wc__db_op_set_changelist(svn_wc__db_t *db,
6161                              const char *local_abspath,
6162                              const char *new_changelist,
6163                              const apr_array_header_t *changelist_filter,
6164                              svn_depth_t depth,
6165                              svn_wc_notify_func2_t notify_func,
6166                              void *notify_baton,
6167                              svn_cancel_func_t cancel_func,
6168                              void *cancel_baton,
6169                              apr_pool_t *scratch_pool)
6170 {
6171   svn_wc__db_wcroot_t *wcroot;
6172   const char *local_relpath;
6173   struct set_changelist_baton_t scb;
6174
6175   scb.new_changelist = new_changelist;
6176   scb.changelist_filter = changelist_filter;
6177   scb.depth = depth;
6178
6179   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6180
6181   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6182                                                 db, local_abspath,
6183                                                 scratch_pool, scratch_pool));
6184   VERIFY_USABLE_WCROOT(wcroot);
6185
6186   /* Flush the entries before we do the work. Even if no work is performed,
6187      the flush isn't a problem. */
6188   SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6189
6190   /* Perform the set-changelist operation (transactionally), perform any
6191      notifications necessary, and then clean out our temporary tables.  */
6192   return svn_error_trace(with_finalization(wcroot, local_relpath,
6193                                            set_changelist_txn, &scb,
6194                                            do_changelist_notify, NULL,
6195                                            cancel_func, cancel_baton,
6196                                            notify_func, notify_baton,
6197                                            STMT_FINALIZE_CHANGELIST,
6198                                            scratch_pool));
6199 }
6200
6201 /* Implementation of svn_wc__db_op_mark_conflict() */
6202 svn_error_t *
6203 svn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t *wcroot,
6204                                   const char *local_relpath,
6205                                   const svn_skel_t *conflict_skel,
6206                                   apr_pool_t *scratch_pool)
6207 {
6208   svn_sqlite__stmt_t *stmt;
6209   svn_boolean_t got_row;
6210   svn_boolean_t is_complete;
6211
6212   SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict_skel));
6213   SVN_ERR_ASSERT(is_complete);
6214
6215   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6216                                     STMT_SELECT_ACTUAL_NODE));
6217   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6218   SVN_ERR(svn_sqlite__step(&got_row, stmt));
6219   SVN_ERR(svn_sqlite__reset(stmt));
6220
6221   if (got_row)
6222     {
6223       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6224                                         STMT_UPDATE_ACTUAL_CONFLICT));
6225       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6226     }
6227   else
6228     {
6229       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6230                                         STMT_INSERT_ACTUAL_CONFLICT));
6231       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6232       if (*local_relpath != '\0')
6233         SVN_ERR(svn_sqlite__bind_text(stmt, 4,
6234                                       svn_relpath_dirname(local_relpath,
6235                                                           scratch_pool)));
6236     }
6237
6238   {
6239     svn_stringbuf_t *sb = svn_skel__unparse(conflict_skel, scratch_pool);
6240
6241     SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6242   }
6243
6244   SVN_ERR(svn_sqlite__update(NULL, stmt));
6245
6246   return SVN_NO_ERROR;
6247 }
6248
6249 svn_error_t *
6250 svn_wc__db_op_mark_conflict(svn_wc__db_t *db,
6251                             const char *local_abspath,
6252                             const svn_skel_t *conflict_skel,
6253                             const svn_skel_t *work_items,
6254                             apr_pool_t *scratch_pool)
6255 {
6256   svn_wc__db_wcroot_t *wcroot;
6257   const char *local_relpath;
6258
6259   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6260
6261   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6262                               local_abspath, scratch_pool, scratch_pool));
6263   VERIFY_USABLE_WCROOT(wcroot);
6264
6265   SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6266                                             conflict_skel, scratch_pool));
6267
6268   /* ### Should be handled in the same transaction as setting the conflict */
6269   if (work_items)
6270     SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6271
6272   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6273
6274   return SVN_NO_ERROR;
6275
6276 }
6277
6278 /* The body of svn_wc__db_op_mark_resolved().
6279  */
6280 static svn_error_t *
6281 db_op_mark_resolved(svn_wc__db_wcroot_t *wcroot,
6282                     const char *local_relpath,
6283                     svn_wc__db_t *db,
6284                     svn_boolean_t resolved_text,
6285                     svn_boolean_t resolved_props,
6286                     svn_boolean_t resolved_tree,
6287                     const svn_skel_t *work_items,
6288                     apr_pool_t *scratch_pool)
6289 {
6290   svn_sqlite__stmt_t *stmt;
6291   svn_boolean_t have_row;
6292   int total_affected_rows = 0;
6293   svn_boolean_t resolved_all;
6294   apr_size_t conflict_len;
6295   const void *conflict_data;
6296   svn_skel_t *conflicts;
6297
6298   /* Check if we have a conflict in ACTUAL */
6299   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6300                                     STMT_SELECT_ACTUAL_NODE));
6301   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6302
6303   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6304
6305   if (! have_row)
6306     {
6307       SVN_ERR(svn_sqlite__reset(stmt));
6308
6309       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6310                                         STMT_SELECT_NODE_INFO));
6311
6312       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6313
6314       SVN_ERR(svn_sqlite__step(&have_row, stmt));
6315       SVN_ERR(svn_sqlite__reset(stmt));
6316
6317       if (have_row)
6318         return SVN_NO_ERROR;
6319
6320       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6321                                _("The node '%s' was not found."),
6322                                    path_for_error_message(wcroot,
6323                                                           local_relpath,
6324                                                           scratch_pool));
6325     }
6326
6327   conflict_data = svn_sqlite__column_blob(stmt, 2, &conflict_len,
6328                                           scratch_pool);
6329   conflicts = svn_skel__parse(conflict_data, conflict_len, scratch_pool);
6330   SVN_ERR(svn_sqlite__reset(stmt));
6331
6332   SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
6333                                         db, wcroot->abspath,
6334                                         resolved_text,
6335                                         resolved_props ? "" : NULL,
6336                                         resolved_tree,
6337                                         scratch_pool, scratch_pool));
6338
6339   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6340                                     STMT_UPDATE_ACTUAL_CONFLICT));
6341   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6342
6343   if (! resolved_all)
6344     {
6345       svn_stringbuf_t *sb = svn_skel__unparse(conflicts, scratch_pool);
6346
6347       SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6348     }
6349
6350   SVN_ERR(svn_sqlite__update(&total_affected_rows, stmt));
6351
6352   /* Now, remove the actual node if it doesn't have any more useful
6353      information.  We only need to do this if we've remove data ourselves. */
6354   if (total_affected_rows > 0)
6355     {
6356       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6357                                         STMT_DELETE_ACTUAL_EMPTY));
6358       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6359       SVN_ERR(svn_sqlite__step_done(stmt));
6360     }
6361
6362   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6363
6364   return SVN_NO_ERROR;
6365 }
6366
6367 svn_error_t *
6368 svn_wc__db_op_mark_resolved(svn_wc__db_t *db,
6369                             const char *local_abspath,
6370                             svn_boolean_t resolved_text,
6371                             svn_boolean_t resolved_props,
6372                             svn_boolean_t resolved_tree,
6373                             const svn_skel_t *work_items,
6374                             apr_pool_t *scratch_pool)
6375 {
6376   svn_wc__db_wcroot_t *wcroot;
6377   const char *local_relpath;
6378
6379   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6380
6381   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6382                               local_abspath, scratch_pool, scratch_pool));
6383   VERIFY_USABLE_WCROOT(wcroot);
6384
6385   SVN_WC__DB_WITH_TXN(
6386     db_op_mark_resolved(wcroot, local_relpath, db,
6387                         resolved_text, resolved_props, resolved_tree,
6388                         work_items, scratch_pool),
6389     wcroot);
6390
6391   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6392   return SVN_NO_ERROR;
6393 }
6394
6395 /* Clear moved-to information at the delete-half of the move which
6396  * moved LOCAL_RELPATH here. This transforms the move into a simple delete. */
6397 static svn_error_t *
6398 clear_moved_to(const char *local_relpath,
6399                svn_wc__db_wcroot_t *wcroot,
6400                apr_pool_t *scratch_pool)
6401 {
6402   svn_sqlite__stmt_t *stmt;
6403   svn_boolean_t have_row;
6404   const char *moved_from_relpath;
6405
6406   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6407                                     STMT_SELECT_MOVED_FROM_RELPATH));
6408   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6409   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6410   if (!have_row)
6411     {
6412       SVN_ERR(svn_sqlite__reset(stmt));
6413       return SVN_NO_ERROR;
6414     }
6415
6416   moved_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
6417   SVN_ERR(svn_sqlite__reset(stmt));
6418
6419   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6420                                     STMT_CLEAR_MOVED_TO_RELPATH));
6421   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6422                             moved_from_relpath,
6423                             relpath_depth(moved_from_relpath)));
6424   SVN_ERR(svn_sqlite__step_done(stmt));
6425
6426   return SVN_NO_ERROR;
6427 }
6428
6429 /* One of the two alternative bodies of svn_wc__db_op_revert().
6430  *
6431  * Implements svn_wc__db_txn_callback_t. */
6432 static svn_error_t *
6433 op_revert_txn(void *baton,
6434               svn_wc__db_wcroot_t *wcroot,
6435               const char *local_relpath,
6436               apr_pool_t *scratch_pool)
6437 {
6438   svn_wc__db_t *db = baton;
6439   svn_sqlite__stmt_t *stmt;
6440   svn_boolean_t have_row;
6441   int op_depth;
6442   svn_boolean_t moved_here;
6443   int affected_rows;
6444   const char *moved_to;
6445
6446   /* ### Similar structure to op_revert_recursive_txn, should they be
6447          combined? */
6448
6449   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6450                                     STMT_SELECT_NODE_INFO));
6451   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6452   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6453   if (!have_row)
6454     {
6455       SVN_ERR(svn_sqlite__reset(stmt));
6456
6457       /* There was no NODE row, so attempt to delete an ACTUAL_NODE row.  */
6458       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6459                                         STMT_DELETE_ACTUAL_NODE));
6460       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6461       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6462       if (affected_rows)
6463         {
6464           /* Can't do non-recursive actual-only revert if actual-only
6465              children exist. Raise an error to cancel the transaction.  */
6466           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6467                                             STMT_ACTUAL_HAS_CHILDREN));
6468           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6469           SVN_ERR(svn_sqlite__step(&have_row, stmt));
6470           SVN_ERR(svn_sqlite__reset(stmt));
6471           if (have_row)
6472             return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6473                                      _("Can't revert '%s' without"
6474                                        " reverting children"),
6475                                      path_for_error_message(wcroot,
6476                                                             local_relpath,
6477                                                             scratch_pool));
6478           return SVN_NO_ERROR;
6479         }
6480
6481       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6482                                _("The node '%s' was not found."),
6483                                path_for_error_message(wcroot,
6484                                                       local_relpath,
6485                                                       scratch_pool));
6486     }
6487
6488   op_depth = svn_sqlite__column_int(stmt, 0);
6489   moved_here = svn_sqlite__column_boolean(stmt, 15);
6490   moved_to = svn_sqlite__column_text(stmt, 17, scratch_pool);
6491   SVN_ERR(svn_sqlite__reset(stmt));
6492
6493   if (moved_to)
6494     {
6495       SVN_ERR(svn_wc__db_resolve_break_moved_away_internal(wcroot,
6496                                                            local_relpath,
6497                                                            op_depth,
6498                                                            scratch_pool));
6499     }
6500   else
6501     {
6502       svn_skel_t *conflict;
6503
6504       SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, wcroot,
6505                                                 local_relpath,
6506                                                 scratch_pool, scratch_pool));
6507       if (conflict)
6508         {
6509           svn_wc_operation_t operation;
6510           svn_boolean_t tree_conflicted;
6511
6512           SVN_ERR(svn_wc__conflict_read_info(&operation, NULL, NULL, NULL,
6513                                              &tree_conflicted,
6514                                              db, wcroot->abspath,
6515                                              conflict,
6516                                              scratch_pool, scratch_pool));
6517           if (tree_conflicted
6518               && (operation == svn_wc_operation_update
6519                   || operation == svn_wc_operation_switch))
6520             {
6521               svn_wc_conflict_reason_t reason;
6522               svn_wc_conflict_action_t action;
6523
6524               SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
6525                                                           NULL,
6526                                                           db, wcroot->abspath,
6527                                                           conflict,
6528                                                           scratch_pool,
6529                                                           scratch_pool));
6530
6531               if (reason == svn_wc_conflict_reason_deleted)
6532                 SVN_ERR(svn_wc__db_resolve_delete_raise_moved_away(
6533                           db, svn_dirent_join(wcroot->abspath, local_relpath,
6534                                               scratch_pool),
6535                           NULL, NULL /* ### How do we notify this? */,
6536                           scratch_pool));
6537             }
6538         }
6539     }
6540
6541   if (op_depth > 0 && op_depth == relpath_depth(local_relpath))
6542     {
6543       /* Can't do non-recursive revert if children exist */
6544       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6545                                         STMT_SELECT_GE_OP_DEPTH_CHILDREN));
6546       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6547                                 local_relpath, op_depth));
6548       SVN_ERR(svn_sqlite__step(&have_row, stmt));
6549       SVN_ERR(svn_sqlite__reset(stmt));
6550       if (have_row)
6551         return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6552                                  _("Can't revert '%s' without"
6553                                    " reverting children"),
6554                                  path_for_error_message(wcroot,
6555                                                         local_relpath,
6556                                                         scratch_pool));
6557
6558       /* Rewrite the op-depth of all deleted children making the
6559          direct children into roots of deletes. */
6560       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6561                                      STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE));
6562       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6563                                 local_relpath,
6564                                 op_depth));
6565       SVN_ERR(svn_sqlite__step_done(stmt));
6566
6567       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6568                                         STMT_DELETE_WORKING_NODE));
6569       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6570       SVN_ERR(svn_sqlite__step_done(stmt));
6571
6572       /* ### This removes the lock, but what about the access baton? */
6573       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6574                                         STMT_DELETE_WC_LOCK_ORPHAN));
6575       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6576       SVN_ERR(svn_sqlite__step_done(stmt));
6577
6578       /* If this node was moved-here, clear moved-to at the move source. */
6579       if (moved_here)
6580         SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool));
6581     }
6582
6583   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6584                                   STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST));
6585   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6586   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6587   if (!affected_rows)
6588     {
6589       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6590                                     STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST));
6591       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6592       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6593     }
6594
6595   return SVN_NO_ERROR;
6596 }
6597
6598
6599 /* One of the two alternative bodies of svn_wc__db_op_revert().
6600  *
6601  * Implements svn_wc__db_txn_callback_t. */
6602 static svn_error_t *
6603 op_revert_recursive_txn(void *baton,
6604                         svn_wc__db_wcroot_t *wcroot,
6605                         const char *local_relpath,
6606                         apr_pool_t *scratch_pool)
6607 {
6608   svn_sqlite__stmt_t *stmt;
6609   svn_boolean_t have_row;
6610   int op_depth;
6611   int select_op_depth;
6612   svn_boolean_t moved_here;
6613   int affected_rows;
6614   apr_pool_t *iterpool;
6615
6616   /* ### Similar structure to op_revert_txn, should they be
6617          combined? */
6618
6619   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6620                                     STMT_SELECT_NODE_INFO));
6621   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6622   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6623   if (!have_row)
6624     {
6625       SVN_ERR(svn_sqlite__reset(stmt));
6626
6627       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6628                                         STMT_DELETE_ACTUAL_NODE));
6629       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
6630                                 local_relpath));
6631       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6632
6633       if (affected_rows)
6634         return SVN_NO_ERROR;  /* actual-only revert */
6635
6636       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6637                                _("The node '%s' was not found."),
6638                                path_for_error_message(wcroot,
6639                                                       local_relpath,
6640                                                       scratch_pool));
6641     }
6642
6643   op_depth = svn_sqlite__column_int(stmt, 0);
6644   moved_here = svn_sqlite__column_boolean(stmt, 15);
6645   SVN_ERR(svn_sqlite__reset(stmt));
6646
6647   if (op_depth > 0 && op_depth != relpath_depth(local_relpath))
6648     return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6649                              _("Can't revert '%s' without"
6650                                " reverting parent"),
6651                              path_for_error_message(wcroot,
6652                                                     local_relpath,
6653                                                     scratch_pool));
6654
6655   /* Remove moved-here from move destinations outside the tree. */
6656   SVN_ERR(svn_sqlite__get_statement(
6657                     &stmt, wcroot->sdb, STMT_SELECT_MOVED_OUTSIDE));
6658   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
6659                             op_depth));
6660   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6661   while (have_row)
6662     {
6663       const char *move_src_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6664       int move_op_depth = svn_sqlite__column_int(stmt, 2);
6665       svn_error_t *err;
6666
6667       err = svn_wc__db_resolve_break_moved_away_internal(wcroot,
6668                                                          move_src_relpath,
6669                                                          move_op_depth,
6670                                                          scratch_pool);
6671       if (err)
6672         return svn_error_compose_create(err, svn_sqlite__reset(stmt));
6673
6674       SVN_ERR(svn_sqlite__step(&have_row, stmt));
6675     }
6676   SVN_ERR(svn_sqlite__reset(stmt));
6677
6678   /* Don't delete BASE nodes */
6679   select_op_depth = op_depth ? op_depth : 1;
6680
6681   /* Reverting any non wc-root node */
6682   SVN_ERR(svn_sqlite__get_statement(
6683                     &stmt, wcroot->sdb,
6684                     STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
6685   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6686                             local_relpath, select_op_depth));
6687   SVN_ERR(svn_sqlite__step_done(stmt));
6688
6689   SVN_ERR(svn_sqlite__get_statement(
6690                     &stmt, wcroot->sdb,
6691                     STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
6692   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6693   SVN_ERR(svn_sqlite__step_done(stmt));
6694
6695   SVN_ERR(svn_sqlite__get_statement(
6696                     &stmt, wcroot->sdb,
6697                     STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
6698   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6699   SVN_ERR(svn_sqlite__step_done(stmt));
6700
6701   /* ### This removes the locks, but what about the access batons? */
6702   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6703                                     STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
6704   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
6705                             local_relpath));
6706   SVN_ERR(svn_sqlite__step_done(stmt));
6707
6708   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6709                                     STMT_SELECT_MOVED_HERE_CHILDREN));
6710   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6711
6712   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6713
6714   iterpool = svn_pool_create(scratch_pool);
6715   while (have_row)
6716     {
6717       const char *moved_here_child_relpath;
6718       svn_error_t *err;
6719
6720       svn_pool_clear(iterpool);
6721
6722       moved_here_child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
6723       err = clear_moved_to(moved_here_child_relpath, wcroot, iterpool);
6724       if (err)
6725         return svn_error_trace(svn_error_compose_create(
6726                                         err,
6727                                         svn_sqlite__reset(stmt)));
6728
6729       SVN_ERR(svn_sqlite__step(&have_row, stmt));
6730     }
6731   SVN_ERR(svn_sqlite__reset(stmt));
6732   svn_pool_destroy(iterpool);
6733
6734   /* Clear potential moved-to pointing at the target node itself. */
6735   if (op_depth > 0 && op_depth == relpath_depth(local_relpath)
6736       && moved_here)
6737     SVN_ERR(clear_moved_to(local_relpath, wcroot, scratch_pool));
6738
6739   return SVN_NO_ERROR;
6740 }
6741
6742 svn_error_t *
6743 svn_wc__db_op_revert(svn_wc__db_t *db,
6744                      const char *local_abspath,
6745                      svn_depth_t depth,
6746                      apr_pool_t *result_pool,
6747                      apr_pool_t *scratch_pool)
6748 {
6749   svn_wc__db_wcroot_t *wcroot;
6750   const char *local_relpath;
6751   struct with_triggers_baton_t wtb = { STMT_CREATE_REVERT_LIST,
6752                                        STMT_DROP_REVERT_LIST_TRIGGERS,
6753                                        NULL, NULL};
6754
6755   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6756
6757   switch (depth)
6758     {
6759     case svn_depth_empty:
6760       wtb.cb_func = op_revert_txn;
6761       wtb.cb_baton = db;
6762       break;
6763     case svn_depth_infinity:
6764       wtb.cb_func = op_revert_recursive_txn;
6765       break;
6766     default:
6767       return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
6768                                _("Unsupported depth for revert of '%s'"),
6769                                svn_dirent_local_style(local_abspath,
6770                                                       scratch_pool));
6771     }
6772
6773   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6774                               db, local_abspath, scratch_pool, scratch_pool));
6775   VERIFY_USABLE_WCROOT(wcroot);
6776
6777   SVN_WC__DB_WITH_TXN(with_triggers(&wtb, wcroot, local_relpath, scratch_pool),
6778                       wcroot);
6779
6780   SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6781
6782   return SVN_NO_ERROR;
6783 }
6784
6785 /* The body of svn_wc__db_revert_list_read().
6786  */
6787 static svn_error_t *
6788 revert_list_read(svn_boolean_t *reverted,
6789                  const apr_array_header_t **marker_paths,
6790                  svn_boolean_t *copied_here,
6791                  svn_node_kind_t *kind,
6792                  svn_wc__db_wcroot_t *wcroot,
6793                  const char *local_relpath,
6794                  svn_wc__db_t *db,
6795                  apr_pool_t *result_pool,
6796                  apr_pool_t *scratch_pool)
6797 {
6798   svn_sqlite__stmt_t *stmt;
6799   svn_boolean_t have_row;
6800
6801   *reverted = FALSE;
6802   *marker_paths = NULL;
6803   *copied_here = FALSE;
6804   *kind = svn_node_unknown;
6805
6806   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6807                                     STMT_SELECT_REVERT_LIST));
6808   SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6809   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6810   if (have_row)
6811     {
6812       svn_boolean_t is_actual = svn_sqlite__column_boolean(stmt, 0);
6813       svn_boolean_t another_row = FALSE;
6814
6815       if (is_actual)
6816         {
6817           apr_size_t conflict_len;
6818           const void *conflict_data;
6819
6820           conflict_data = svn_sqlite__column_blob(stmt, 5, &conflict_len,
6821                                                   scratch_pool);
6822           if (conflict_data)
6823             {
6824               svn_skel_t *conflicts = svn_skel__parse(conflict_data,
6825                                                       conflict_len,
6826                                                       scratch_pool);
6827
6828               SVN_ERR(svn_wc__conflict_read_markers(marker_paths,
6829                                                     db, wcroot->abspath,
6830                                                     conflicts,
6831                                                     result_pool,
6832                                                     scratch_pool));
6833             }
6834
6835           if (!svn_sqlite__column_is_null(stmt, 1)) /* notify */
6836             *reverted = TRUE;
6837
6838           SVN_ERR(svn_sqlite__step(&another_row, stmt));
6839         }
6840
6841       if (!is_actual || another_row)
6842         {
6843           *reverted = TRUE;
6844           if (!svn_sqlite__column_is_null(stmt, 4)) /* repos_id */
6845             {
6846               int op_depth = svn_sqlite__column_int(stmt, 3);
6847               *copied_here = (op_depth == relpath_depth(local_relpath));
6848             }
6849           *kind = svn_sqlite__column_token(stmt, 2, kind_map);
6850         }
6851
6852     }
6853   SVN_ERR(svn_sqlite__reset(stmt));
6854
6855   if (have_row)
6856     {
6857       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6858                                         STMT_DELETE_REVERT_LIST));
6859       SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6860       SVN_ERR(svn_sqlite__step_done(stmt));
6861     }
6862
6863   return SVN_NO_ERROR;
6864 }
6865
6866 svn_error_t *
6867 svn_wc__db_revert_list_read(svn_boolean_t *reverted,
6868                             const apr_array_header_t **marker_files,
6869                             svn_boolean_t *copied_here,
6870                             svn_node_kind_t *kind,
6871                             svn_wc__db_t *db,
6872                             const char *local_abspath,
6873                             apr_pool_t *result_pool,
6874                             apr_pool_t *scratch_pool)
6875 {
6876   svn_wc__db_wcroot_t *wcroot;
6877   const char *local_relpath;
6878
6879   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6880                               db, local_abspath, scratch_pool, scratch_pool));
6881   VERIFY_USABLE_WCROOT(wcroot);
6882
6883   SVN_WC__DB_WITH_TXN(
6884     revert_list_read(reverted, marker_files, copied_here, kind,
6885                      wcroot, local_relpath, db,
6886                      result_pool, scratch_pool),
6887     wcroot);
6888   return SVN_NO_ERROR;
6889 }
6890
6891
6892 /* The body of svn_wc__db_revert_list_read_copied_children().
6893  */
6894 static svn_error_t *
6895 revert_list_read_copied_children(svn_wc__db_wcroot_t *wcroot,
6896                                  const char *local_relpath,
6897                                  const apr_array_header_t **children_p,
6898                                  apr_pool_t *result_pool,
6899                                  apr_pool_t *scratch_pool)
6900 {
6901   svn_sqlite__stmt_t *stmt;
6902   svn_boolean_t have_row;
6903   apr_array_header_t *children;
6904
6905   children =
6906     apr_array_make(result_pool, 0,
6907                   sizeof(svn_wc__db_revert_list_copied_child_info_t *));
6908
6909   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6910                                     STMT_SELECT_REVERT_LIST_COPIED_CHILDREN));
6911   SVN_ERR(svn_sqlite__bindf(stmt, "sd",
6912                             local_relpath, relpath_depth(local_relpath)));
6913   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6914   while (have_row)
6915     {
6916       svn_wc__db_revert_list_copied_child_info_t *child_info;
6917       const char *child_relpath;
6918
6919       child_info = apr_palloc(result_pool, sizeof(*child_info));
6920
6921       child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6922       child_info->abspath = svn_dirent_join(wcroot->abspath, child_relpath,
6923                                             result_pool);
6924       child_info->kind = svn_sqlite__column_token(stmt, 1, kind_map);
6925       APR_ARRAY_PUSH(
6926         children,
6927         svn_wc__db_revert_list_copied_child_info_t *) = child_info;
6928
6929       SVN_ERR(svn_sqlite__step(&have_row, stmt));
6930     }
6931    SVN_ERR(svn_sqlite__reset(stmt));
6932
6933   *children_p = children;
6934
6935   return SVN_NO_ERROR;
6936 }
6937
6938
6939 svn_error_t *
6940 svn_wc__db_revert_list_read_copied_children(const apr_array_header_t **children,
6941                                             svn_wc__db_t *db,
6942                                             const char *local_abspath,
6943                                             apr_pool_t *result_pool,
6944                                             apr_pool_t *scratch_pool)
6945 {
6946   svn_wc__db_wcroot_t *wcroot;
6947   const char *local_relpath;
6948
6949   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6950                               db, local_abspath, scratch_pool, scratch_pool));
6951   VERIFY_USABLE_WCROOT(wcroot);
6952
6953   SVN_WC__DB_WITH_TXN(
6954     revert_list_read_copied_children(wcroot, local_relpath, children,
6955                                      result_pool, scratch_pool),
6956     wcroot);
6957   return SVN_NO_ERROR;
6958 }
6959
6960
6961 svn_error_t *
6962 svn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func,
6963                               void *notify_baton,
6964                               svn_wc__db_t *db,
6965                               const char *local_abspath,
6966                               apr_pool_t *scratch_pool)
6967 {
6968   svn_wc__db_wcroot_t *wcroot;
6969   const char *local_relpath;
6970   svn_sqlite__stmt_t *stmt;
6971   svn_boolean_t have_row;
6972   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
6973
6974   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6975                               db, local_abspath, scratch_pool, iterpool));
6976   VERIFY_USABLE_WCROOT(wcroot);
6977
6978   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6979                                     STMT_SELECT_REVERT_LIST_RECURSIVE));
6980   SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
6981   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6982   if (!have_row)
6983     return svn_error_trace(svn_sqlite__reset(stmt)); /* optimise for no row */
6984   while (have_row)
6985     {
6986       const char *notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
6987
6988       svn_pool_clear(iterpool);
6989
6990       notify_func(notify_baton,
6991                   svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
6992                                                        notify_relpath,
6993                                                        iterpool),
6994                                        svn_wc_notify_revert,
6995                                        iterpool),
6996                   iterpool);
6997
6998       SVN_ERR(svn_sqlite__step(&have_row, stmt));
6999     }
7000   SVN_ERR(svn_sqlite__reset(stmt));
7001
7002   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7003                                     STMT_DELETE_REVERT_LIST_RECURSIVE));
7004   SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7005   SVN_ERR(svn_sqlite__step_done(stmt));
7006
7007   svn_pool_destroy(iterpool);
7008
7009   return SVN_NO_ERROR;
7010 }
7011
7012 svn_error_t *
7013 svn_wc__db_revert_list_done(svn_wc__db_t *db,
7014                             const char *local_abspath,
7015                             apr_pool_t *scratch_pool)
7016 {
7017   svn_wc__db_wcroot_t *wcroot;
7018   const char *local_relpath;
7019
7020   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7021                               db, local_abspath, scratch_pool, scratch_pool));
7022   VERIFY_USABLE_WCROOT(wcroot);
7023
7024   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_DROP_REVERT_LIST));
7025
7026   return SVN_NO_ERROR;
7027 }
7028
7029 /* The body of svn_wc__db_op_remove_node().
7030  */
7031 static svn_error_t *
7032 remove_node_txn(svn_boolean_t *left_changes,
7033                 svn_wc__db_wcroot_t *wcroot,
7034                 const char *local_relpath,
7035                 svn_wc__db_t *db,
7036                 svn_boolean_t destroy_wc,
7037                 svn_boolean_t destroy_changes,
7038                 svn_revnum_t not_present_rev,
7039                 svn_wc__db_status_t not_present_status,
7040                 svn_node_kind_t not_present_kind,
7041                 const svn_skel_t *conflict,
7042                 const svn_skel_t *work_items,
7043                 svn_cancel_func_t cancel_func,
7044                 void *cancel_baton,
7045                 apr_pool_t *scratch_pool)
7046 {
7047   svn_sqlite__stmt_t *stmt;
7048
7049   apr_int64_t repos_id;
7050   const char *repos_relpath;
7051
7052   /* Note that unlike many similar functions it is a valid scenario for this
7053      function to be called on a wcroot! */
7054
7055    /* db set when destroying wc */
7056   SVN_ERR_ASSERT(!destroy_wc || db != NULL);
7057
7058   if (left_changes)
7059     *left_changes = FALSE;
7060
7061   /* Need info for not_present node? */
7062   if (SVN_IS_VALID_REVNUM(not_present_rev))
7063     SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
7064                                               &repos_relpath, &repos_id,
7065                                               NULL, NULL, NULL, NULL, NULL,
7066                                               NULL, NULL, NULL, NULL, NULL,
7067                                               wcroot, local_relpath,
7068                                               scratch_pool, scratch_pool));
7069
7070   if (destroy_wc
7071       && (!destroy_changes || *local_relpath == '\0'))
7072     {
7073       svn_boolean_t have_row;
7074       apr_pool_t *iterpool;
7075       svn_error_t *err = NULL;
7076
7077       /* Install WQ items for deleting the unmodified files and all dirs */
7078       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7079                                         STMT_SELECT_WORKING_PRESENT));
7080       SVN_ERR(svn_sqlite__bindf(stmt, "is",
7081                                 wcroot->wc_id, local_relpath));
7082
7083       SVN_ERR(svn_sqlite__step(&have_row, stmt));
7084
7085       iterpool = svn_pool_create(scratch_pool);
7086
7087       while (have_row)
7088         {
7089           const char *child_relpath;
7090           const char *child_abspath;
7091           svn_node_kind_t child_kind;
7092           svn_boolean_t have_checksum;
7093           svn_filesize_t recorded_size;
7094           apr_int64_t recorded_time;
7095           const svn_io_dirent2_t *dirent;
7096           svn_boolean_t modified_p = TRUE;
7097           svn_skel_t *work_item = NULL;
7098
7099           svn_pool_clear(iterpool);
7100
7101           child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7102           child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
7103
7104           child_abspath = svn_dirent_join(wcroot->abspath, child_relpath,
7105                                           iterpool);
7106
7107           if (child_kind == svn_node_file)
7108             {
7109               have_checksum = !svn_sqlite__column_is_null(stmt, 2);
7110               recorded_size = get_recorded_size(stmt, 3);
7111               recorded_time = svn_sqlite__column_int64(stmt, 4);
7112             }
7113
7114           if (cancel_func)
7115             err = cancel_func(cancel_baton);
7116
7117           if (err)
7118             break;
7119
7120           err = svn_io_stat_dirent2(&dirent, child_abspath, FALSE, TRUE,
7121                                     iterpool, iterpool);
7122
7123           if (err)
7124             break;
7125
7126           if (destroy_changes
7127               || dirent->kind != svn_node_file
7128               || child_kind != svn_node_file)
7129             {
7130               /* Not interested in keeping changes */
7131               modified_p = FALSE;
7132             }
7133           else if (child_kind == svn_node_file
7134                    && dirent->kind == svn_node_file
7135                    && dirent->filesize == recorded_size
7136                    && dirent->mtime == recorded_time)
7137             {
7138               modified_p = FALSE; /* File matches recorded state */
7139             }
7140           else if (have_checksum)
7141             err = svn_wc__internal_file_modified_p(&modified_p,
7142                                                    db, child_abspath,
7143                                                    FALSE, iterpool);
7144
7145           if (err)
7146             break;
7147
7148           if (modified_p)
7149             {
7150               if (left_changes)
7151                 *left_changes = TRUE;
7152             }
7153           else if (child_kind == svn_node_dir)
7154             {
7155               err = svn_wc__wq_build_dir_remove(&work_item,
7156                                                 db, wcroot->abspath,
7157                                                 child_abspath, FALSE,
7158                                                 iterpool, iterpool);
7159             }
7160           else /* svn_node_file || svn_node_symlink */
7161             {
7162               err = svn_wc__wq_build_file_remove(&work_item,
7163                                                  db, wcroot->abspath,
7164                                                  child_abspath,
7165                                                  iterpool, iterpool);
7166             }
7167
7168           if (err)
7169             break;
7170
7171           if (work_item)
7172             {
7173               err = add_work_items(wcroot->sdb, work_item, iterpool);
7174               if (err)
7175                 break;
7176             }
7177
7178           SVN_ERR(svn_sqlite__step(&have_row, stmt));
7179         }
7180       svn_pool_destroy(iterpool);
7181
7182       SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
7183     }
7184
7185   if (destroy_wc && *local_relpath != '\0')
7186     {
7187       /* Create work item for destroying the root */
7188       svn_wc__db_status_t status;
7189       svn_node_kind_t kind;
7190       SVN_ERR(read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, NULL,
7191                         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7192                         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7193                         wcroot, local_relpath,
7194                         scratch_pool, scratch_pool));
7195
7196       if (status == svn_wc__db_status_normal
7197           || status == svn_wc__db_status_added
7198           || status == svn_wc__db_status_incomplete)
7199         {
7200           svn_skel_t *work_item = NULL;
7201           const char *local_abspath = svn_dirent_join(wcroot->abspath,
7202                                                           local_relpath,
7203                                                           scratch_pool);
7204
7205           if (kind == svn_node_dir)
7206             {
7207               SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
7208                                                   db, wcroot->abspath,
7209                                                   local_abspath,
7210                                                   destroy_changes
7211                                                       /* recursive */,
7212                                                   scratch_pool, scratch_pool));
7213             }
7214           else
7215             {
7216               svn_boolean_t modified_p = FALSE;
7217
7218               if (!destroy_changes)
7219                 {
7220                   SVN_ERR(svn_wc__internal_file_modified_p(&modified_p,
7221                                                            db, local_abspath,
7222                                                            FALSE,
7223                                                            scratch_pool));
7224                 }
7225
7226               if (!modified_p)
7227                 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
7228                                                      db, wcroot->abspath,
7229                                                      local_abspath,
7230                                                      scratch_pool,
7231                                                      scratch_pool));
7232               else
7233                 {
7234                   if (left_changes)
7235                     *left_changes = TRUE;
7236                 }
7237             }
7238
7239           SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
7240         }
7241     }
7242
7243   /* Remove all nodes below local_relpath */
7244   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7245                                     STMT_DELETE_NODE_RECURSIVE));
7246   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7247   SVN_ERR(svn_sqlite__step_done(stmt));
7248
7249   /* Delete the root NODE when this is not the working copy root */
7250   if (local_relpath[0] != '\0')
7251     {
7252       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7253                                         STMT_DELETE_NODE_ALL_LAYERS));
7254       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7255       SVN_ERR(svn_sqlite__step_done(stmt));
7256     }
7257
7258   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7259                                     STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7260
7261   /* Delete all actual nodes at or below local_relpath */
7262   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7263                                          local_relpath));
7264   SVN_ERR(svn_sqlite__step_done(stmt));
7265
7266   /* Should we leave a not-present node? */
7267   if (SVN_IS_VALID_REVNUM(not_present_rev))
7268     {
7269       insert_base_baton_t ibb;
7270       blank_ibb(&ibb);
7271
7272       ibb.repos_id = repos_id;
7273
7274       SVN_ERR_ASSERT(not_present_status == svn_wc__db_status_not_present
7275                      || not_present_status == svn_wc__db_status_excluded);
7276
7277       ibb.status = not_present_status;
7278       ibb.kind = not_present_kind;
7279
7280       ibb.repos_relpath = repos_relpath;
7281       ibb.revision = not_present_rev;
7282
7283       SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
7284     }
7285
7286   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
7287   if (conflict)
7288     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
7289                                               conflict, scratch_pool));
7290
7291   return SVN_NO_ERROR;
7292 }
7293
7294 svn_error_t *
7295 svn_wc__db_op_remove_node(svn_boolean_t *left_changes,
7296                           svn_wc__db_t *db,
7297                           const char *local_abspath,
7298                           svn_boolean_t destroy_wc,
7299                           svn_boolean_t destroy_changes,
7300                           svn_revnum_t not_present_revision,
7301                           svn_wc__db_status_t not_present_status,
7302                           svn_node_kind_t not_present_kind,
7303                           const svn_skel_t *conflict,
7304                           const svn_skel_t *work_items,
7305                           svn_cancel_func_t cancel_func,
7306                           void *cancel_baton,
7307                           apr_pool_t *scratch_pool)
7308 {
7309   svn_wc__db_wcroot_t *wcroot;
7310   const char *local_relpath;
7311
7312   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7313
7314   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7315                               local_abspath, scratch_pool, scratch_pool));
7316   VERIFY_USABLE_WCROOT(wcroot);
7317
7318   SVN_WC__DB_WITH_TXN(remove_node_txn(left_changes,
7319                                       wcroot, local_relpath, db,
7320                                       destroy_wc, destroy_changes,
7321                                       not_present_revision, not_present_status,
7322                                       not_present_kind, conflict, work_items,
7323                                       cancel_func, cancel_baton, scratch_pool),
7324                       wcroot);
7325
7326   /* Flush everything below this node in all ways */
7327   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
7328                         scratch_pool));
7329
7330   return SVN_NO_ERROR;
7331 }
7332
7333
7334 /* The body of svn_wc__db_op_set_base_depth().
7335  */
7336 static svn_error_t *
7337 db_op_set_base_depth(svn_wc__db_wcroot_t *wcroot,
7338                      const char *local_relpath,
7339                      svn_depth_t depth,
7340                      apr_pool_t *scratch_pool)
7341 {
7342   svn_sqlite__stmt_t *stmt;
7343   int affected_rows;
7344
7345   /* Flush any entries before we start monkeying the database.  */
7346   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7347                                     STMT_UPDATE_NODE_BASE_DEPTH));
7348   SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
7349                             svn_token__to_word(depth_map, depth)));
7350   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7351
7352   if (affected_rows == 0)
7353     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7354                              "The node '%s' is not a committed directory",
7355                              path_for_error_message(wcroot, local_relpath,
7356                                                     scratch_pool));
7357
7358   return SVN_NO_ERROR;
7359 }
7360
7361
7362 svn_error_t *
7363 svn_wc__db_op_set_base_depth(svn_wc__db_t *db,
7364                              const char *local_abspath,
7365                              svn_depth_t depth,
7366                              apr_pool_t *scratch_pool)
7367 {
7368   svn_wc__db_wcroot_t *wcroot;
7369   const char *local_relpath;
7370
7371   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7372   SVN_ERR_ASSERT(depth >= svn_depth_empty && depth <= svn_depth_infinity);
7373
7374   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7375                               local_abspath, scratch_pool, scratch_pool));
7376   VERIFY_USABLE_WCROOT(wcroot);
7377
7378   /* ### We set depth on working and base to match entry behavior.
7379          Maybe these should be separated later? */
7380   SVN_WC__DB_WITH_TXN(db_op_set_base_depth(wcroot, local_relpath, depth,
7381                                            scratch_pool),
7382                       wcroot);
7383
7384   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
7385
7386   return SVN_NO_ERROR;
7387 }
7388
7389
7390 static svn_error_t *
7391 info_below_working(svn_boolean_t *have_base,
7392                    svn_boolean_t *have_work,
7393                    svn_wc__db_status_t *status,
7394                    svn_wc__db_wcroot_t *wcroot,
7395                    const char *local_relpath,
7396                    int below_op_depth, /* < 0 is ignored */
7397                    apr_pool_t *scratch_pool);
7398
7399
7400 /* Convert STATUS, the raw status obtained from the presence map, to
7401    the status appropriate for a working (op_depth > 0) node and return
7402    it in *WORKING_STATUS. */
7403 static svn_error_t *
7404 convert_to_working_status(svn_wc__db_status_t *working_status,
7405                           svn_wc__db_status_t status)
7406 {
7407   svn_wc__db_status_t work_status = status;
7408
7409   SVN_ERR_ASSERT(work_status == svn_wc__db_status_normal
7410                  || work_status == svn_wc__db_status_not_present
7411                  || work_status == svn_wc__db_status_base_deleted
7412                  || work_status == svn_wc__db_status_incomplete
7413                  || work_status == svn_wc__db_status_excluded);
7414
7415   if (work_status == svn_wc__db_status_excluded)
7416     {
7417       *working_status = svn_wc__db_status_excluded;
7418     }
7419   else if (work_status == svn_wc__db_status_not_present
7420            || work_status == svn_wc__db_status_base_deleted)
7421     {
7422       /* The caller should scan upwards to detect whether this
7423          deletion has occurred because this node has been moved
7424          away, or it is a regular deletion. Also note that the
7425          deletion could be of the BASE tree, or a child of
7426          something that has been copied/moved here. */
7427
7428       *working_status = svn_wc__db_status_deleted;
7429     }
7430   else /* normal or incomplete */
7431     {
7432       /* The caller should scan upwards to detect whether this
7433          addition has occurred because of a simple addition,
7434          a copy, or is the destination of a move. */
7435       *working_status = svn_wc__db_status_added;
7436     }
7437
7438   return SVN_NO_ERROR;
7439 }
7440
7441
7442 /* Return the status of the node, if any, below the "working" node (or
7443    below BELOW_OP_DEPTH if >= 0).
7444    Set *HAVE_BASE or *HAVE_WORK to indicate if a base node or lower
7445    working node is present, and *STATUS to the status of the first
7446    layer below the selected node. */
7447 static svn_error_t *
7448 info_below_working(svn_boolean_t *have_base,
7449                    svn_boolean_t *have_work,
7450                    svn_wc__db_status_t *status,
7451                    svn_wc__db_wcroot_t *wcroot,
7452                    const char *local_relpath,
7453                    int below_op_depth,
7454                    apr_pool_t *scratch_pool)
7455 {
7456   svn_sqlite__stmt_t *stmt;
7457   svn_boolean_t have_row;
7458
7459   *have_base = *have_work =  FALSE;
7460   *status = svn_wc__db_status_normal;
7461
7462   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7463                                     STMT_SELECT_NODE_INFO));
7464   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7465   SVN_ERR(svn_sqlite__step(&have_row, stmt));
7466
7467   if (below_op_depth >= 0)
7468     {
7469       while (have_row &&
7470              (svn_sqlite__column_int(stmt, 0) > below_op_depth))
7471         {
7472           SVN_ERR(svn_sqlite__step(&have_row, stmt));
7473         }
7474     }
7475   if (have_row)
7476     {
7477       SVN_ERR(svn_sqlite__step(&have_row, stmt));
7478       if (have_row)
7479         *status = svn_sqlite__column_token(stmt, 3, presence_map);
7480
7481       while (have_row)
7482         {
7483           int op_depth = svn_sqlite__column_int(stmt, 0);
7484
7485           if (op_depth > 0)
7486             *have_work = TRUE;
7487           else
7488             *have_base = TRUE;
7489
7490           SVN_ERR(svn_sqlite__step(&have_row, stmt));
7491         }
7492     }
7493   SVN_ERR(svn_sqlite__reset(stmt));
7494
7495   if (*have_work)
7496     SVN_ERR(convert_to_working_status(status, *status));
7497
7498   return SVN_NO_ERROR;
7499 }
7500
7501 /* Helper function for op_delete_txn */
7502 static svn_error_t *
7503 delete_update_movedto(svn_wc__db_wcroot_t *wcroot,
7504                       const char *child_moved_from_relpath,
7505                       int op_depth,
7506                       const char *new_moved_to_relpath,
7507                       apr_pool_t *scratch_pool)
7508 {
7509   svn_sqlite__stmt_t *stmt;
7510   int affected;
7511
7512   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7513                                     STMT_UPDATE_MOVED_TO_RELPATH));
7514
7515   SVN_ERR(svn_sqlite__bindf(stmt, "isds",
7516                             wcroot->wc_id,
7517                             child_moved_from_relpath,
7518                             op_depth,
7519                             new_moved_to_relpath));
7520   SVN_ERR(svn_sqlite__update(&affected, stmt));
7521   assert(affected == 1);
7522
7523   return SVN_NO_ERROR;
7524 }
7525
7526
7527 struct op_delete_baton_t {
7528   const char *moved_to_relpath; /* NULL if delete is not part of a move */
7529   svn_skel_t *conflict;
7530   svn_skel_t *work_items;
7531   svn_boolean_t delete_dir_externals;
7532   svn_boolean_t notify;
7533 };
7534
7535 /* This structure is used while rewriting move information for nodes.
7536  *
7537  * The most simple case of rewriting move information happens when
7538  * a moved-away subtree is moved again:  mv A B; mv B C
7539  * The second move requires rewriting moved-to info at or within A.
7540  *
7541  * Another example is a move of a subtree which had nodes moved into it:
7542  *   mv A B/F; mv B G
7543  * This requires rewriting such that A/F is marked has having moved to G/F.
7544  *
7545  * Another case is where a node becomes a nested moved node.
7546  * A nested move happens when a subtree child is moved before or after
7547  * the subtree itself is moved. For example:
7548  *   mv A/F A/G; mv A B
7549  * In this case, the move A/F -> A/G is rewritten to B/F -> B/G.
7550  * Note that the following sequence results in the same DB state:
7551  *   mv A B; mv B/F B/G
7552  * We do not care about the order the moves were performed in.
7553  * For details, see http://wiki.apache.org/subversion/MultiLayerMoves
7554  */
7555 struct moved_node_t {
7556   /* The source of the move. */
7557   const char *local_relpath;
7558
7559   /* The move destination. */
7560   const char *moved_to_relpath;
7561
7562   /* The op-depth of the deleted node at the source of the move. */
7563   int op_depth;
7564
7565   /* When >= 1 the op_depth at which local_relpath was moved to its
7566      location. Used to find its original location outside the delete */
7567   int moved_from_depth;
7568 };
7569
7570 /* Helper function to resolve the original location of local_relpath at OP_DEPTH
7571    before it was moved into the tree rooted at ROOT_RELPATH. */
7572 static svn_error_t *
7573 resolve_moved_from(const char **moved_from_relpath,
7574                    int *moved_from_op_depth,
7575                    svn_wc__db_wcroot_t *wcroot,
7576                    const char *root_relpath,
7577                    const char *local_relpath,
7578                    int op_depth,
7579                    apr_pool_t *result_pool,
7580                    apr_pool_t *scratch_pool)
7581 {
7582   const char *suffix = "";
7583   svn_sqlite__stmt_t *stmt;
7584   const char *m_from_relpath;
7585   int m_from_op_depth;
7586   int m_move_from_depth;
7587   svn_boolean_t have_row;
7588
7589   while (relpath_depth(local_relpath) > op_depth)
7590     {
7591       const char *name;
7592       svn_relpath_split(&local_relpath, &name, local_relpath, scratch_pool);
7593       suffix = svn_relpath_join(suffix, name, scratch_pool);
7594     }
7595
7596   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7597                                     STMT_SELECT_MOVED_FROM_FOR_DELETE));
7598   SVN_ERR(svn_sqlite__bindf(stmt, "is",
7599                             wcroot->wc_id, local_relpath));
7600   SVN_ERR(svn_sqlite__step(&have_row, stmt));
7601
7602   if (!have_row)
7603     {
7604       /* assert(have_row); */
7605       *moved_from_relpath = NULL;
7606       *moved_from_op_depth = -1;
7607
7608       SVN_ERR(svn_sqlite__reset(stmt));
7609
7610       return SVN_NO_ERROR;
7611     }
7612
7613   m_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
7614   m_from_op_depth = svn_sqlite__column_int(stmt, 1);
7615   m_move_from_depth = svn_sqlite__column_int(stmt, 2);
7616
7617   SVN_ERR(svn_sqlite__reset(stmt));
7618
7619   if (! svn_relpath_skip_ancestor(root_relpath, m_from_relpath))
7620     {
7621       *moved_from_relpath = svn_relpath_join(m_from_relpath, suffix,
7622                                              result_pool);
7623       *moved_from_op_depth = m_from_op_depth; /* ### Ok? */
7624       return SVN_NO_ERROR;
7625     }
7626   else if (!m_move_from_depth)
7627     {
7628       *moved_from_relpath = NULL;
7629       *moved_from_op_depth = -1;
7630       return SVN_NO_ERROR;
7631     }
7632
7633   return svn_error_trace(
7634         resolve_moved_from(moved_from_relpath,
7635                            moved_from_op_depth,
7636                            wcroot,
7637                            root_relpath,
7638                            svn_relpath_join(m_from_relpath, suffix,
7639                                             scratch_pool),
7640                            m_move_from_depth,
7641                            result_pool, scratch_pool));
7642 }
7643
7644 static svn_error_t *
7645 delete_node(void *baton,
7646             svn_wc__db_wcroot_t *wcroot,
7647             const char *local_relpath,
7648             apr_pool_t *scratch_pool)
7649 {
7650   struct op_delete_baton_t *b = baton;
7651   svn_wc__db_status_t status;
7652   svn_boolean_t have_row, op_root;
7653   svn_boolean_t add_work = FALSE;
7654   svn_sqlite__stmt_t *stmt;
7655   int working_op_depth; /* Depth of what is to be deleted */
7656   int keep_op_depth = 0; /* Depth of what is below what is deleted */
7657   svn_node_kind_t kind;
7658   apr_array_header_t *moved_nodes = NULL;
7659   int delete_op_depth = relpath_depth(local_relpath);
7660
7661   assert(*local_relpath); /* Can't delete wcroot */
7662
7663   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7664                                     STMT_SELECT_NODE_INFO));
7665   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7666   SVN_ERR(svn_sqlite__step(&have_row, stmt));
7667
7668   if (!have_row)
7669     {
7670       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
7671                                svn_sqlite__reset(stmt),
7672                                _("The node '%s' was not found."),
7673                                path_for_error_message(wcroot,
7674                                                       local_relpath,
7675                                                       scratch_pool));
7676     }
7677
7678   working_op_depth = svn_sqlite__column_int(stmt, 0);
7679   status = svn_sqlite__column_token(stmt, 3, presence_map);
7680   kind = svn_sqlite__column_token(stmt, 4, kind_map);
7681
7682   if (working_op_depth < delete_op_depth)
7683     {
7684       op_root = FALSE;
7685       add_work = TRUE;
7686       keep_op_depth = working_op_depth;
7687     }
7688   else
7689     {
7690       op_root = TRUE;
7691
7692       SVN_ERR(svn_sqlite__step(&have_row, stmt));
7693
7694       if (have_row)
7695         {
7696           svn_wc__db_status_t below_status;
7697           int below_op_depth;
7698
7699           below_op_depth = svn_sqlite__column_int(stmt, 0);
7700           below_status = svn_sqlite__column_token(stmt, 3, presence_map);
7701
7702           if (below_status != svn_wc__db_status_not_present
7703               && below_status != svn_wc__db_status_base_deleted)
7704             {
7705               add_work = TRUE;
7706               keep_op_depth = below_op_depth;
7707             }
7708           else
7709             keep_op_depth = 0;
7710         }
7711       else
7712         keep_op_depth = -1;
7713     }
7714
7715   SVN_ERR(svn_sqlite__reset(stmt));
7716
7717   if (working_op_depth != 0) /* WORKING */
7718     SVN_ERR(convert_to_working_status(&status, status));
7719
7720   if (status == svn_wc__db_status_deleted
7721       || status == svn_wc__db_status_not_present)
7722     return SVN_NO_ERROR;
7723
7724   /* Don't copy BASE directories with server excluded nodes */
7725   if (status == svn_wc__db_status_normal && kind == svn_node_dir)
7726     {
7727       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7728                                         STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
7729       SVN_ERR(svn_sqlite__bindf(stmt, "is",
7730                                 wcroot->wc_id, local_relpath));
7731       SVN_ERR(svn_sqlite__step(&have_row, stmt));
7732       if (have_row)
7733         {
7734           const char *absent_path = svn_sqlite__column_text(stmt, 0,
7735                                                             scratch_pool);
7736
7737           return svn_error_createf(
7738                                SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
7739                                svn_sqlite__reset(stmt),
7740                           _("Cannot delete '%s' as '%s' is excluded by server"),
7741                                path_for_error_message(wcroot, local_relpath,
7742                                                       scratch_pool),
7743                                path_for_error_message(wcroot, absent_path,
7744                                                       scratch_pool));
7745         }
7746       SVN_ERR(svn_sqlite__reset(stmt));
7747     }
7748   else if (status == svn_wc__db_status_server_excluded)
7749     {
7750       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
7751                           _("Cannot delete '%s' as it is excluded by server"),
7752                                path_for_error_message(wcroot, local_relpath,
7753                                                       scratch_pool));
7754     }
7755   else if (status == svn_wc__db_status_excluded)
7756     {
7757       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
7758                           _("Cannot delete '%s' as it is excluded"),
7759                                path_for_error_message(wcroot, local_relpath,
7760                                                       scratch_pool));
7761     }
7762
7763   if (b->moved_to_relpath)
7764     {
7765       const char *moved_from_relpath = NULL;
7766       struct moved_node_t *moved_node;
7767       int move_op_depth;
7768
7769       moved_nodes = apr_array_make(scratch_pool, 1,
7770                                    sizeof(struct moved_node_t *));
7771
7772       /* The node is being moved-away.
7773        * Figure out if the node was moved-here before, or whether this
7774        * is the first time the node is moved. */
7775       if (status == svn_wc__db_status_added)
7776         SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL,
7777                               &moved_from_relpath,
7778                               NULL,
7779                               &move_op_depth,
7780                               wcroot, local_relpath,
7781                               scratch_pool, scratch_pool));
7782
7783       if (op_root && moved_from_relpath)
7784         {
7785           const char *part = svn_relpath_skip_ancestor(local_relpath,
7786                                                        moved_from_relpath);
7787
7788           /* Existing move-root is moved to another location */
7789           moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
7790           if (!part)
7791             moved_node->local_relpath = moved_from_relpath;
7792           else
7793             moved_node->local_relpath = svn_relpath_join(b->moved_to_relpath,
7794                                                          part, scratch_pool);
7795           moved_node->op_depth = move_op_depth;
7796           moved_node->moved_to_relpath = b->moved_to_relpath;
7797           moved_node->moved_from_depth = -1;
7798
7799           APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
7800         }
7801       else if (!op_root && (status == svn_wc__db_status_normal
7802                             || status == svn_wc__db_status_copied
7803                             || status == svn_wc__db_status_moved_here))
7804         {
7805           /* The node is becoming a move-root for the first time,
7806            * possibly because of a nested move operation. */
7807           moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
7808           moved_node->local_relpath = local_relpath;
7809           moved_node->op_depth = delete_op_depth;
7810           moved_node->moved_to_relpath = b->moved_to_relpath;
7811           moved_node->moved_from_depth = -1;
7812
7813           APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
7814         }
7815       /* Else: We can't track history of local additions and/or of things we are
7816                about to delete. */
7817
7818       /* And update all moved_to values still pointing to this location */
7819       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7820                                         STMT_UPDATE_MOVED_TO_DESCENDANTS));
7821       SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
7822                                              local_relpath,
7823                                              b->moved_to_relpath));
7824       SVN_ERR(svn_sqlite__update(NULL, stmt));
7825     }
7826
7827   /* Find children that were moved out of the subtree rooted at this node.
7828    * We'll need to update their op-depth columns because their deletion
7829    * is now implied by the deletion of their parent (i.e. this node). */
7830     {
7831       apr_pool_t *iterpool;
7832       int i;
7833
7834       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7835                                         STMT_SELECT_MOVED_FOR_DELETE));
7836       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
7837                                 delete_op_depth));
7838
7839       SVN_ERR(svn_sqlite__step(&have_row, stmt));
7840       iterpool = svn_pool_create(scratch_pool);
7841       while (have_row)
7842         {
7843           struct moved_node_t *mn;
7844           const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7845           const char *mv_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
7846           int child_op_depth = svn_sqlite__column_int(stmt, 2);
7847           int moved_from_depth = -1;
7848           svn_boolean_t fixup = FALSE;
7849
7850           if (! b->moved_to_relpath
7851               && ! svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
7852             {
7853               /* a NULL moved_here_depth will be reported as 0 */
7854               int moved_here_depth = svn_sqlite__column_int(stmt, 3);
7855
7856               /* Plain delete. Fixup move information of descendants that were
7857                  moved here, or that were moved out */
7858
7859               if (moved_here_depth >= delete_op_depth)
7860                 {
7861                   /* The move we recorded here must be moved to the location
7862                      this node had before it was moved here.
7863
7864                      This might contain multiple steps when the node was moved
7865                      in several places within the to be deleted tree */
7866
7867                   /* ### TODO: Add logic */
7868                   fixup = TRUE;
7869                   moved_from_depth = moved_here_depth;
7870                 }
7871               else
7872                 {
7873                   /* Update the op-depth of an moved away node that was
7874                      registered as moved by the records that we are about
7875                      to delete */
7876                   fixup = TRUE;
7877                   child_op_depth = delete_op_depth;
7878                 }
7879             }
7880           else if (b->moved_to_relpath)
7881             {
7882               /* The node is moved to a new location */
7883
7884               if (delete_op_depth == child_op_depth)
7885                 {
7886                   /* Update the op-depth of a tree shadowed by this tree */
7887                   fixup = TRUE;
7888                   /*child_op_depth = delete_depth;*/
7889                 }
7890               else if (child_op_depth >= delete_op_depth
7891                        && !svn_relpath_skip_ancestor(local_relpath,
7892                                                      mv_to_relpath))
7893                 {
7894                   /* Update the move destination of something that is now moved
7895                      away further */
7896
7897                   child_relpath = svn_relpath_skip_ancestor(local_relpath,
7898                                                             child_relpath);
7899
7900                   if (child_relpath)
7901                     {
7902                       child_relpath = svn_relpath_join(b->moved_to_relpath,
7903                                                        child_relpath,
7904                                                        scratch_pool);
7905
7906                       if (child_op_depth > delete_op_depth
7907                            && svn_relpath_skip_ancestor(local_relpath,
7908                                                         child_relpath))
7909                         child_op_depth = delete_op_depth;
7910                       else
7911                         child_op_depth = relpath_depth(child_relpath);
7912
7913                       fixup = TRUE;
7914                     }
7915                 }
7916             }
7917
7918           if (fixup)
7919             {
7920               mn = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
7921
7922               mn->local_relpath = apr_pstrdup(scratch_pool, child_relpath);
7923               mn->moved_to_relpath = apr_pstrdup(scratch_pool, mv_to_relpath);
7924               mn->op_depth = child_op_depth;
7925               mn->moved_from_depth = moved_from_depth;
7926
7927               if (!moved_nodes)
7928                 moved_nodes = apr_array_make(scratch_pool, 1,
7929                                              sizeof(struct moved_node_t *));
7930               APR_ARRAY_PUSH(moved_nodes, struct moved_node_t *) = mn;
7931             }
7932
7933           SVN_ERR(svn_sqlite__step(&have_row, stmt));
7934         }
7935       SVN_ERR(svn_sqlite__reset(stmt));
7936
7937       for (i = 0; moved_nodes && (i < moved_nodes->nelts); i++)
7938         {
7939           struct moved_node_t *mn = APR_ARRAY_IDX(moved_nodes, i,
7940                                                   struct moved_node_t *);
7941
7942           if (mn->moved_from_depth > 0)
7943             {
7944               svn_pool_clear(iterpool);
7945
7946               SVN_ERR(resolve_moved_from(&mn->local_relpath, &mn->op_depth,
7947                                          wcroot, local_relpath,
7948                                          mn->local_relpath,
7949                                          mn->moved_from_depth,
7950                                          scratch_pool, iterpool));
7951
7952               if (!mn->local_relpath)
7953                 svn_sort__array_delete(moved_nodes, i--, 1);
7954             }
7955         }
7956
7957       svn_pool_destroy(iterpool);
7958     }
7959
7960   if (!b->moved_to_relpath)
7961     {
7962       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7963                                         STMT_CLEAR_MOVED_TO_DESCENDANTS));
7964       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7965                                 local_relpath));
7966       SVN_ERR(svn_sqlite__update(NULL, stmt));
7967
7968       if (op_root)
7969         {
7970           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7971                                             STMT_CLEAR_MOVED_TO_FROM_DEST));
7972           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7973                                     local_relpath));
7974
7975           SVN_ERR(svn_sqlite__update(NULL, stmt));
7976         }
7977     }
7978
7979
7980   /* ### Put actual-only nodes into the list? */
7981   if (b->notify)
7982     {
7983       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7984                                         STMT_INSERT_DELETE_LIST));
7985       SVN_ERR(svn_sqlite__bindf(stmt, "isd",
7986                                 wcroot->wc_id, local_relpath, working_op_depth));
7987       SVN_ERR(svn_sqlite__step_done(stmt));
7988     }
7989
7990   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7991                                     STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
7992   SVN_ERR(svn_sqlite__bindf(stmt, "isd",
7993                             wcroot->wc_id, local_relpath, delete_op_depth));
7994   SVN_ERR(svn_sqlite__step_done(stmt));
7995
7996   /* Delete ACTUAL_NODE rows, but leave those that have changelist
7997      and a NODES row. */
7998   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7999                          STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8000   SVN_ERR(svn_sqlite__bindf(stmt, "is",
8001                             wcroot->wc_id, local_relpath));
8002   SVN_ERR(svn_sqlite__step_done(stmt));
8003
8004   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8005                          STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8006   SVN_ERR(svn_sqlite__bindf(stmt, "is",
8007                             wcroot->wc_id, local_relpath));
8008   SVN_ERR(svn_sqlite__step_done(stmt));
8009
8010   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8011                                     STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
8012   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8013                             local_relpath));
8014   SVN_ERR(svn_sqlite__step_done(stmt));
8015
8016   if (add_work)
8017     {
8018       /* Delete the node at LOCAL_RELPATH, and possibly mark it as moved. */
8019
8020       /* Delete the node and possible descendants. */
8021       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8022                                  STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
8023       SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
8024                                 wcroot->wc_id, local_relpath,
8025                                 keep_op_depth, delete_op_depth));
8026       SVN_ERR(svn_sqlite__step_done(stmt));
8027     }
8028
8029   if (moved_nodes)
8030     {
8031       int i;
8032
8033       for (i = 0; i < moved_nodes->nelts; ++i)
8034         {
8035           const struct moved_node_t *moved_node
8036             = APR_ARRAY_IDX(moved_nodes, i, void *);
8037
8038           SVN_ERR(delete_update_movedto(wcroot,
8039                                         moved_node->local_relpath,
8040                                         moved_node->op_depth,
8041                                         moved_node->moved_to_relpath,
8042                                         scratch_pool));
8043         }
8044     }
8045
8046   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8047                                     STMT_DELETE_FILE_EXTERNALS));
8048   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8049   SVN_ERR(svn_sqlite__step_done(stmt));
8050
8051   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8052                                     b->delete_dir_externals
8053                                     ? STMT_DELETE_EXTERNAL_REGISTATIONS
8054                                     : STMT_DELETE_FILE_EXTERNAL_REGISTATIONS));
8055   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8056   SVN_ERR(svn_sqlite__step_done(stmt));
8057
8058   SVN_ERR(add_work_items(wcroot->sdb, b->work_items, scratch_pool));
8059   if (b->conflict)
8060     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
8061                                               b->conflict, scratch_pool));
8062
8063   return SVN_NO_ERROR;
8064 }
8065
8066 static svn_error_t *
8067 op_delete_txn(void *baton,
8068               svn_wc__db_wcroot_t *wcroot,
8069               const char *local_relpath,
8070               apr_pool_t *scratch_pool)
8071 {
8072
8073   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8074   SVN_ERR(delete_node(baton, wcroot, local_relpath, scratch_pool));
8075   return SVN_NO_ERROR;
8076 }
8077
8078
8079 struct op_delete_many_baton_t {
8080   apr_array_header_t *rel_targets;
8081   svn_boolean_t delete_dir_externals;
8082   const svn_skel_t *work_items;
8083 } op_delete_many_baton_t;
8084
8085 static svn_error_t *
8086 op_delete_many_txn(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_many_baton_t *odmb = baton;
8092   struct op_delete_baton_t odb;
8093   int i;
8094   apr_pool_t *iterpool;
8095
8096   odb.moved_to_relpath = NULL;
8097   odb.conflict = NULL;
8098   odb.work_items = NULL;
8099   odb.delete_dir_externals = odmb->delete_dir_externals;
8100   odb.notify = TRUE;
8101
8102   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8103   iterpool = svn_pool_create(scratch_pool);
8104   for (i = 0; i < odmb->rel_targets->nelts; i++)
8105     {
8106       const char *target_relpath = APR_ARRAY_IDX(odmb->rel_targets, i,
8107                                                  const char *);
8108
8109
8110       svn_pool_clear(iterpool);
8111       SVN_ERR(delete_node(&odb, wcroot, target_relpath, iterpool));
8112     }
8113   svn_pool_destroy(iterpool);
8114
8115   SVN_ERR(add_work_items(wcroot->sdb, odmb->work_items, scratch_pool));
8116
8117   return SVN_NO_ERROR;
8118 }
8119
8120
8121 static svn_error_t *
8122 do_delete_notify(void *baton,
8123                  svn_wc__db_wcroot_t *wcroot,
8124                  svn_cancel_func_t cancel_func,
8125                  void *cancel_baton,
8126                  svn_wc_notify_func2_t notify_func,
8127                  void *notify_baton,
8128                  apr_pool_t *scratch_pool)
8129 {
8130   svn_sqlite__stmt_t *stmt;
8131   svn_boolean_t have_row;
8132   apr_pool_t *iterpool;
8133
8134   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8135                                     STMT_SELECT_DELETE_LIST));
8136   SVN_ERR(svn_sqlite__step(&have_row, stmt));
8137
8138   iterpool = svn_pool_create(scratch_pool);
8139   while (have_row)
8140     {
8141       const char *notify_relpath;
8142       const char *notify_abspath;
8143
8144       svn_pool_clear(iterpool);
8145
8146       notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8147       notify_abspath = svn_dirent_join(wcroot->abspath,
8148                                        notify_relpath,
8149                                        iterpool);
8150
8151       notify_func(notify_baton,
8152                   svn_wc_create_notify(notify_abspath,
8153                                        svn_wc_notify_delete,
8154                                        iterpool),
8155                   iterpool);
8156
8157       SVN_ERR(svn_sqlite__step(&have_row, stmt));
8158     }
8159   svn_pool_destroy(iterpool);
8160
8161   SVN_ERR(svn_sqlite__reset(stmt));
8162
8163   /* We only allow cancellation after notification for all deleted nodes
8164    * has happened. The nodes are already deleted so we should notify for
8165    * all of them. */
8166   if (cancel_func)
8167     SVN_ERR(cancel_func(cancel_baton));
8168
8169   return SVN_NO_ERROR;
8170 }
8171
8172
8173 svn_error_t *
8174 svn_wc__db_op_delete(svn_wc__db_t *db,
8175                      const char *local_abspath,
8176                      const char *moved_to_abspath,
8177                      svn_boolean_t delete_dir_externals,
8178                      svn_skel_t *conflict,
8179                      svn_skel_t *work_items,
8180                      svn_cancel_func_t cancel_func,
8181                      void *cancel_baton,
8182                      svn_wc_notify_func2_t notify_func,
8183                      void *notify_baton,
8184                      apr_pool_t *scratch_pool)
8185 {
8186   svn_wc__db_wcroot_t *wcroot;
8187   svn_wc__db_wcroot_t *moved_to_wcroot;
8188   const char *local_relpath;
8189   const char *moved_to_relpath;
8190   struct op_delete_baton_t odb;
8191
8192   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8193
8194   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8195                                                 db, local_abspath,
8196                                                 scratch_pool, scratch_pool));
8197   VERIFY_USABLE_WCROOT(wcroot);
8198
8199   if (moved_to_abspath)
8200     {
8201       SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&moved_to_wcroot,
8202                                                     &moved_to_relpath,
8203                                                     db, moved_to_abspath,
8204                                                     scratch_pool,
8205                                                     scratch_pool));
8206       VERIFY_USABLE_WCROOT(moved_to_wcroot);
8207
8208       if (strcmp(wcroot->abspath, moved_to_wcroot->abspath) != 0)
8209         return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
8210                                  _("Cannot move '%s' to '%s' because they "
8211                                    "are not in the same working copy"),
8212                                  svn_dirent_local_style(local_abspath,
8213                                                         scratch_pool),
8214                                  svn_dirent_local_style(moved_to_abspath,
8215                                                         scratch_pool));
8216     }
8217   else
8218     moved_to_relpath = NULL;
8219
8220   odb.moved_to_relpath = moved_to_relpath;
8221   odb.conflict = conflict;
8222   odb.work_items = work_items;
8223   odb.delete_dir_externals = delete_dir_externals;
8224
8225   if (notify_func)
8226     {
8227       /* Perform the deletion operation (transactionally), perform any
8228          notifications necessary, and then clean out our temporary tables.  */
8229       odb.notify = TRUE;
8230       SVN_ERR(with_finalization(wcroot, local_relpath,
8231                                 op_delete_txn, &odb,
8232                                 do_delete_notify, NULL,
8233                                 cancel_func, cancel_baton,
8234                                 notify_func, notify_baton,
8235                                 STMT_FINALIZE_DELETE,
8236                                 scratch_pool));
8237     }
8238   else
8239     {
8240       /* Avoid the trigger work */
8241       odb.notify = FALSE;
8242       SVN_WC__DB_WITH_TXN(
8243                     delete_node(&odb, wcroot, local_relpath, scratch_pool),
8244                     wcroot);
8245     }
8246
8247   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
8248                         scratch_pool));
8249
8250   return SVN_NO_ERROR;
8251 }
8252
8253
8254 svn_error_t *
8255 svn_wc__db_op_delete_many(svn_wc__db_t *db,
8256                           apr_array_header_t *targets,
8257                           svn_boolean_t delete_dir_externals,
8258                           const svn_skel_t *work_items,
8259                           svn_cancel_func_t cancel_func,
8260                           void *cancel_baton,
8261                           svn_wc_notify_func2_t notify_func,
8262                           void *notify_baton,
8263                           apr_pool_t *scratch_pool)
8264 {
8265   svn_wc__db_wcroot_t *wcroot;
8266   const char *local_relpath;
8267   struct op_delete_many_baton_t odmb;
8268   int i;
8269   apr_pool_t *iterpool;
8270
8271   odmb.rel_targets = apr_array_make(scratch_pool, targets->nelts,
8272                                     sizeof(const char *));
8273   odmb.work_items = work_items;
8274   odmb.delete_dir_externals = delete_dir_externals;
8275   iterpool = svn_pool_create(scratch_pool);
8276   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8277                                                 db,
8278                                                 APR_ARRAY_IDX(targets, 0,
8279                                                               const char *),
8280                                                 scratch_pool, iterpool));
8281   VERIFY_USABLE_WCROOT(wcroot);
8282   for (i = 0; i < targets->nelts; i++)
8283     {
8284       const char *local_abspath = APR_ARRAY_IDX(targets, i, const char*);
8285       svn_wc__db_wcroot_t *target_wcroot;
8286
8287       svn_pool_clear(iterpool);
8288
8289       SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&target_wcroot,
8290                                                     &local_relpath, db,
8291                                                     APR_ARRAY_IDX(targets, i,
8292                                                                   const char *),
8293                                                     scratch_pool, iterpool));
8294       VERIFY_USABLE_WCROOT(target_wcroot);
8295       SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8296
8297       /* Assert that all targets are within the same working copy. */
8298       SVN_ERR_ASSERT(wcroot->wc_id == target_wcroot->wc_id);
8299
8300       APR_ARRAY_PUSH(odmb.rel_targets, const char *) = local_relpath;
8301       SVN_ERR(flush_entries(target_wcroot, local_abspath, svn_depth_infinity,
8302                             iterpool));
8303
8304     }
8305   svn_pool_destroy(iterpool);
8306
8307   /* Perform the deletion operation (transactionally), perform any
8308      notifications necessary, and then clean out our temporary tables.  */
8309   return svn_error_trace(with_finalization(wcroot, wcroot->abspath,
8310                                            op_delete_many_txn, &odmb,
8311                                            do_delete_notify, NULL,
8312                                            cancel_func, cancel_baton,
8313                                            notify_func, notify_baton,
8314                                            STMT_FINALIZE_DELETE,
8315                                            scratch_pool));
8316 }
8317
8318
8319 /* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of
8320    DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */
8321 static svn_error_t *
8322 read_info(svn_wc__db_status_t *status,
8323           svn_node_kind_t *kind,
8324           svn_revnum_t *revision,
8325           const char **repos_relpath,
8326           apr_int64_t *repos_id,
8327           svn_revnum_t *changed_rev,
8328           apr_time_t *changed_date,
8329           const char **changed_author,
8330           svn_depth_t *depth,
8331           const svn_checksum_t **checksum,
8332           const char **target,
8333           const char **original_repos_relpath,
8334           apr_int64_t *original_repos_id,
8335           svn_revnum_t *original_revision,
8336           svn_wc__db_lock_t **lock,
8337           svn_filesize_t *recorded_size,
8338           apr_time_t *recorded_time,
8339           const char **changelist,
8340           svn_boolean_t *conflicted,
8341           svn_boolean_t *op_root,
8342           svn_boolean_t *had_props,
8343           svn_boolean_t *props_mod,
8344           svn_boolean_t *have_base,
8345           svn_boolean_t *have_more_work,
8346           svn_boolean_t *have_work,
8347           svn_wc__db_wcroot_t *wcroot,
8348           const char *local_relpath,
8349           apr_pool_t *result_pool,
8350           apr_pool_t *scratch_pool)
8351 {
8352   svn_sqlite__stmt_t *stmt_info;
8353   svn_sqlite__stmt_t *stmt_act;
8354   svn_boolean_t have_info;
8355   svn_boolean_t have_act;
8356   svn_error_t *err = NULL;
8357
8358   /* Obtain the most likely to exist record first, to make sure we don't
8359      have to obtain the SQLite read-lock multiple times */
8360   SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
8361                                     lock ? STMT_SELECT_NODE_INFO_WITH_LOCK
8362                                          : STMT_SELECT_NODE_INFO));
8363   SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
8364   SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
8365
8366   if (changelist || conflicted || props_mod)
8367     {
8368       SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
8369                                         STMT_SELECT_ACTUAL_NODE));
8370       SVN_ERR(svn_sqlite__bindf(stmt_act, "is", wcroot->wc_id, local_relpath));
8371       SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
8372     }
8373   else
8374     {
8375       have_act = FALSE;
8376       stmt_act = NULL;
8377     }
8378
8379   if (have_info)
8380     {
8381       int op_depth;
8382       svn_node_kind_t node_kind;
8383
8384       op_depth = svn_sqlite__column_int(stmt_info, 0);
8385       node_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
8386
8387       if (status)
8388         {
8389           *status = svn_sqlite__column_token(stmt_info, 3, presence_map);
8390
8391           if (op_depth != 0) /* WORKING */
8392             err = svn_error_compose_create(err,
8393                                            convert_to_working_status(status,
8394                                                                      *status));
8395         }
8396       if (kind)
8397         {
8398           *kind = node_kind;
8399         }
8400       if (op_depth != 0)
8401         {
8402           if (repos_id)
8403             *repos_id = INVALID_REPOS_ID;
8404           if (revision)
8405             *revision = SVN_INVALID_REVNUM;
8406           if (repos_relpath)
8407             /* Our path is implied by our parent somewhere up the tree.
8408                With the NULL value and status, the caller will know to
8409                search up the tree for the base of our path.  */
8410             *repos_relpath = NULL;
8411         }
8412       else
8413         {
8414           /* Fetch repository information. If we have a
8415              WORKING_NODE (and have been added), then the repository
8416              we're being added to will be dependent upon a parent. The
8417              caller can scan upwards to locate the repository.  */
8418           repos_location_from_columns(repos_id, revision, repos_relpath,
8419                                       stmt_info, 1, 5, 2, result_pool);
8420         }
8421       if (changed_rev)
8422         {
8423           *changed_rev = svn_sqlite__column_revnum(stmt_info, 8);
8424         }
8425       if (changed_date)
8426         {
8427           *changed_date = svn_sqlite__column_int64(stmt_info, 9);
8428         }
8429       if (changed_author)
8430         {
8431           *changed_author = svn_sqlite__column_text(stmt_info, 10,
8432                                                     result_pool);
8433         }
8434       if (recorded_time)
8435         {
8436           *recorded_time = svn_sqlite__column_int64(stmt_info, 13);
8437         }
8438       if (depth)
8439         {
8440           if (node_kind != svn_node_dir)
8441             {
8442               *depth = svn_depth_unknown;
8443             }
8444           else
8445             {
8446               *depth = svn_sqlite__column_token_null(stmt_info, 11, depth_map,
8447                                                      svn_depth_unknown);
8448             }
8449         }
8450       if (checksum)
8451         {
8452           if (node_kind != svn_node_file)
8453             {
8454               *checksum = NULL;
8455             }
8456           else
8457             {
8458
8459               err = svn_error_compose_create(
8460                         err, svn_sqlite__column_checksum(checksum, stmt_info, 6,
8461                                                          result_pool));
8462             }
8463         }
8464       if (recorded_size)
8465         {
8466           *recorded_size = get_recorded_size(stmt_info, 7);
8467         }
8468       if (target)
8469         {
8470           if (node_kind != svn_node_symlink)
8471             *target = NULL;
8472           else
8473             *target = svn_sqlite__column_text(stmt_info, 12, result_pool);
8474         }
8475       if (changelist)
8476         {
8477           if (have_act)
8478             *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8479           else
8480             *changelist = NULL;
8481         }
8482       if (op_depth == 0)
8483         {
8484           if (original_repos_id)
8485             *original_repos_id = INVALID_REPOS_ID;
8486           if (original_revision)
8487             *original_revision = SVN_INVALID_REVNUM;
8488           if (original_repos_relpath)
8489             *original_repos_relpath = NULL;
8490         }
8491       else
8492         {
8493           repos_location_from_columns(original_repos_id,
8494                                       original_revision,
8495                                       original_repos_relpath,
8496                                       stmt_info, 1, 5, 2, result_pool);
8497         }
8498       if (props_mod)
8499         {
8500           *props_mod = have_act && !svn_sqlite__column_is_null(stmt_act, 1);
8501         }
8502       if (had_props)
8503         {
8504           *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt_info, 14);
8505         }
8506       if (conflicted)
8507         {
8508           if (have_act)
8509             {
8510               *conflicted =
8511                  !svn_sqlite__column_is_null(stmt_act, 2); /* conflict_data */
8512             }
8513           else
8514             *conflicted = FALSE;
8515         }
8516
8517       if (lock)
8518         {
8519           if (op_depth != 0)
8520             *lock = NULL;
8521           else
8522             *lock = lock_from_columns(stmt_info, 17, 18, 19, 20, result_pool);
8523         }
8524
8525       if (have_work)
8526         *have_work = (op_depth != 0);
8527
8528       if (op_root)
8529         {
8530           *op_root = ((op_depth > 0)
8531                       && (op_depth == relpath_depth(local_relpath)));
8532         }
8533
8534       if (have_base || have_more_work)
8535         {
8536           if (have_more_work)
8537             *have_more_work = FALSE;
8538
8539           while (!err && op_depth != 0)
8540             {
8541               err = svn_sqlite__step(&have_info, stmt_info);
8542
8543               if (err || !have_info)
8544                 break;
8545
8546               op_depth = svn_sqlite__column_int(stmt_info, 0);
8547
8548               if (have_more_work)
8549                 {
8550                   if (op_depth > 0)
8551                     *have_more_work = TRUE;
8552
8553                   if (!have_base)
8554                    break;
8555                 }
8556             }
8557
8558           if (have_base)
8559             *have_base = (op_depth == 0);
8560         }
8561     }
8562   else if (have_act)
8563     {
8564       /* A row in ACTUAL_NODE should never exist without a corresponding
8565          node in BASE_NODE and/or WORKING_NODE unless it flags a tree conflict. */
8566       if (svn_sqlite__column_is_null(stmt_act, 2)) /* conflict_data */
8567           err = svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
8568                                   _("Corrupt data for '%s'"),
8569                                   path_for_error_message(wcroot, local_relpath,
8570                                                          scratch_pool));
8571       /* ### What should we return?  Should we have a separate
8572              function for reading actual-only nodes? */
8573
8574       /* As a safety measure, until we decide if we want to use
8575          read_info for actual-only nodes, make sure the caller asked
8576          for the conflict status. */
8577       SVN_ERR_ASSERT(conflicted);
8578
8579       if (status)
8580         *status = svn_wc__db_status_normal;  /* What! No it's not! */
8581       if (kind)
8582         *kind = svn_node_unknown;
8583       if (revision)
8584         *revision = SVN_INVALID_REVNUM;
8585       if (repos_relpath)
8586         *repos_relpath = NULL;
8587       if (repos_id)
8588         *repos_id = INVALID_REPOS_ID;
8589       if (changed_rev)
8590         *changed_rev = SVN_INVALID_REVNUM;
8591       if (changed_date)
8592         *changed_date = 0;
8593       if (depth)
8594         *depth = svn_depth_unknown;
8595       if (checksum)
8596         *checksum = NULL;
8597       if (target)
8598         *target = NULL;
8599       if (original_repos_relpath)
8600         *original_repos_relpath = NULL;
8601       if (original_repos_id)
8602         *original_repos_id = INVALID_REPOS_ID;
8603       if (original_revision)
8604         *original_revision = SVN_INVALID_REVNUM;
8605       if (lock)
8606         *lock = NULL;
8607       if (recorded_size)
8608         *recorded_size = 0;
8609       if (recorded_time)
8610         *recorded_time = 0;
8611       if (changelist)
8612         *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8613       if (op_root)
8614         *op_root = FALSE;
8615       if (had_props)
8616         *had_props = FALSE;
8617       if (props_mod)
8618         *props_mod = FALSE;
8619       if (conflicted)
8620         *conflicted = TRUE;
8621       if (have_base)
8622         *have_base = FALSE;
8623       if (have_more_work)
8624         *have_more_work = FALSE;
8625       if (have_work)
8626         *have_work = FALSE;
8627     }
8628   else
8629     {
8630       err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
8631                               _("The node '%s' was not found."),
8632                               path_for_error_message(wcroot, local_relpath,
8633                                                      scratch_pool));
8634     }
8635
8636   if (stmt_act != NULL)
8637     err = svn_error_compose_create(err, svn_sqlite__reset(stmt_act));
8638
8639   if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
8640     err = svn_error_quick_wrap(err,
8641                                apr_psprintf(scratch_pool,
8642                                             "Error reading node '%s'",
8643                                             local_relpath));
8644
8645   SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info)));
8646
8647   return SVN_NO_ERROR;
8648 }
8649
8650
8651 svn_error_t *
8652 svn_wc__db_read_info_internal(svn_wc__db_status_t *status,
8653                               svn_node_kind_t *kind,
8654                               svn_revnum_t *revision,
8655                               const char **repos_relpath,
8656                               apr_int64_t *repos_id,
8657                               svn_revnum_t *changed_rev,
8658                               apr_time_t *changed_date,
8659                               const char **changed_author,
8660                               svn_depth_t *depth,
8661                               const svn_checksum_t **checksum,
8662                               const char **target,
8663                               const char **original_repos_relpath,
8664                               apr_int64_t *original_repos_id,
8665                               svn_revnum_t *original_revision,
8666                               svn_wc__db_lock_t **lock,
8667                               svn_filesize_t *recorded_size,
8668                               apr_time_t *recorded_time,
8669                               const char **changelist,
8670                               svn_boolean_t *conflicted,
8671                               svn_boolean_t *op_root,
8672                               svn_boolean_t *had_props,
8673                               svn_boolean_t *props_mod,
8674                               svn_boolean_t *have_base,
8675                               svn_boolean_t *have_more_work,
8676                               svn_boolean_t *have_work,
8677                               svn_wc__db_wcroot_t *wcroot,
8678                               const char *local_relpath,
8679                               apr_pool_t *result_pool,
8680                               apr_pool_t *scratch_pool)
8681 {
8682   return svn_error_trace(
8683            read_info(status, kind, revision, repos_relpath, repos_id,
8684                      changed_rev, changed_date, changed_author,
8685                      depth, checksum, target, original_repos_relpath,
8686                      original_repos_id, original_revision, lock,
8687                      recorded_size, recorded_time, changelist, conflicted,
8688                      op_root, had_props, props_mod,
8689                      have_base, have_more_work, have_work,
8690                      wcroot, local_relpath, result_pool, scratch_pool));
8691 }
8692
8693
8694 svn_error_t *
8695 svn_wc__db_read_info(svn_wc__db_status_t *status,
8696                      svn_node_kind_t *kind,
8697                      svn_revnum_t *revision,
8698                      const char **repos_relpath,
8699                      const char **repos_root_url,
8700                      const char **repos_uuid,
8701                      svn_revnum_t *changed_rev,
8702                      apr_time_t *changed_date,
8703                      const char **changed_author,
8704                      svn_depth_t *depth,
8705                      const svn_checksum_t **checksum,
8706                      const char **target,
8707                      const char **original_repos_relpath,
8708                      const char **original_root_url,
8709                      const char **original_uuid,
8710                      svn_revnum_t *original_revision,
8711                      svn_wc__db_lock_t **lock,
8712                      svn_filesize_t *recorded_size,
8713                      apr_time_t *recorded_time,
8714                      const char **changelist,
8715                      svn_boolean_t *conflicted,
8716                      svn_boolean_t *op_root,
8717                      svn_boolean_t *have_props,
8718                      svn_boolean_t *props_mod,
8719                      svn_boolean_t *have_base,
8720                      svn_boolean_t *have_more_work,
8721                      svn_boolean_t *have_work,
8722                      svn_wc__db_t *db,
8723                      const char *local_abspath,
8724                      apr_pool_t *result_pool,
8725                      apr_pool_t *scratch_pool)
8726 {
8727   svn_wc__db_wcroot_t *wcroot;
8728   const char *local_relpath;
8729   apr_int64_t repos_id, original_repos_id;
8730
8731   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8732
8733   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
8734                               local_abspath, scratch_pool, scratch_pool));
8735   VERIFY_USABLE_WCROOT(wcroot);
8736
8737   SVN_ERR(read_info(status, kind, revision, repos_relpath, &repos_id,
8738                     changed_rev, changed_date, changed_author,
8739                     depth, checksum, target, original_repos_relpath,
8740                     &original_repos_id, original_revision, lock,
8741                     recorded_size, recorded_time, changelist, conflicted,
8742                     op_root, have_props, props_mod,
8743                     have_base, have_more_work, have_work,
8744                     wcroot, local_relpath, result_pool, scratch_pool));
8745   SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
8746                                       wcroot->sdb, repos_id, result_pool));
8747   SVN_ERR(svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
8748                                       wcroot->sdb, original_repos_id,
8749                                       result_pool));
8750
8751   return SVN_NO_ERROR;
8752 }
8753
8754 static svn_error_t *
8755 is_wclocked(svn_boolean_t *locked,
8756             svn_wc__db_wcroot_t *wcroot,
8757             const char *dir_relpath,
8758             apr_pool_t *scratch_pool);
8759
8760 /* What we really want to store about a node.  This relies on the
8761    offset of svn_wc__db_info_t being zero. */
8762 struct read_children_info_item_t
8763 {
8764   struct svn_wc__db_info_t info;
8765   int op_depth;
8766   int nr_layers;
8767 };
8768
8769 static svn_error_t *
8770 read_children_info(svn_wc__db_wcroot_t *wcroot,
8771                    const char *dir_relpath,
8772                    apr_hash_t *conflicts,
8773                    apr_hash_t *nodes,
8774                    apr_pool_t *result_pool,
8775                    apr_pool_t *scratch_pool)
8776 {
8777   svn_sqlite__stmt_t *stmt;
8778   svn_boolean_t have_row;
8779   const char *repos_root_url = NULL;
8780   const char *repos_uuid = NULL;
8781   apr_int64_t last_repos_id = INVALID_REPOS_ID;
8782
8783   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8784                                     STMT_SELECT_NODE_CHILDREN_INFO));
8785   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
8786   SVN_ERR(svn_sqlite__step(&have_row, stmt));
8787
8788   while (have_row)
8789     {
8790       /* CHILD item points to what we have about the node. We only provide
8791          CHILD->item to our caller. */
8792       struct read_children_info_item_t *child_item;
8793       const char *child_relpath = svn_sqlite__column_text(stmt, 19, NULL);
8794       const char *name = svn_relpath_basename(child_relpath, NULL);
8795       svn_error_t *err;
8796       int op_depth;
8797       svn_boolean_t new_child;
8798
8799       child_item = svn_hash_gets(nodes, name);
8800       if (child_item)
8801         new_child = FALSE;
8802       else
8803         {
8804           child_item = apr_pcalloc(result_pool, sizeof(*child_item));
8805           new_child = TRUE;
8806         }
8807
8808       op_depth = svn_sqlite__column_int(stmt, 0);
8809
8810       /* Do we have new or better information? */
8811       if (new_child || op_depth > child_item->op_depth)
8812         {
8813           struct svn_wc__db_info_t *child = &child_item->info;
8814           child_item->op_depth = op_depth;
8815
8816           child->kind = svn_sqlite__column_token(stmt, 4, kind_map);
8817
8818           child->status = svn_sqlite__column_token(stmt, 3, presence_map);
8819           if (op_depth != 0)
8820             {
8821               if (child->status == svn_wc__db_status_incomplete)
8822                 child->incomplete = TRUE;
8823               err = convert_to_working_status(&child->status, child->status);
8824               if (err)
8825                 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8826             }
8827
8828           if (op_depth != 0)
8829             child->revnum = SVN_INVALID_REVNUM;
8830           else
8831             child->revnum = svn_sqlite__column_revnum(stmt, 5);
8832
8833           if (op_depth != 0)
8834             child->repos_relpath = NULL;
8835           else
8836             child->repos_relpath = svn_sqlite__column_text(stmt, 2,
8837                                                            result_pool);
8838
8839           if (op_depth != 0 || svn_sqlite__column_is_null(stmt, 1))
8840             {
8841               child->repos_root_url = NULL;
8842               child->repos_uuid = NULL;
8843             }
8844           else
8845             {
8846               const char *last_repos_root_url = NULL;
8847
8848               apr_int64_t repos_id = svn_sqlite__column_int64(stmt, 1);
8849               if (!repos_root_url ||
8850                   (last_repos_id != INVALID_REPOS_ID &&
8851                    repos_id != last_repos_id))
8852                 {
8853                   last_repos_root_url = repos_root_url;
8854                   err = svn_wc__db_fetch_repos_info(&repos_root_url,
8855                                                     &repos_uuid,
8856                                                     wcroot->sdb, repos_id,
8857                                                     result_pool);
8858                   if (err)
8859                     SVN_ERR(svn_error_compose_create(err,
8860                                                  svn_sqlite__reset(stmt)));
8861                 }
8862
8863               if (last_repos_id == INVALID_REPOS_ID)
8864                 last_repos_id = repos_id;
8865
8866               /* Assume working copy is all one repos_id so that a
8867                  single cached value is sufficient. */
8868               if (repos_id != last_repos_id)
8869                 {
8870                   err= svn_error_createf(
8871                          SVN_ERR_WC_DB_ERROR, NULL,
8872                          _("The node '%s' comes from unexpected repository "
8873                            "'%s', expected '%s'; if this node is a file "
8874                            "external using the correct URL in the external "
8875                            "definition can fix the problem, see issue #4087"),
8876                          child_relpath, repos_root_url, last_repos_root_url);
8877                   return svn_error_compose_create(err, svn_sqlite__reset(stmt));
8878                 }
8879               child->repos_root_url = repos_root_url;
8880               child->repos_uuid = repos_uuid;
8881             }
8882
8883           child->changed_rev = svn_sqlite__column_revnum(stmt, 8);
8884
8885           child->changed_date = svn_sqlite__column_int64(stmt, 9);
8886
8887           child->changed_author = svn_sqlite__column_text(stmt, 10,
8888                                                           result_pool);
8889
8890           if (child->kind != svn_node_dir)
8891             child->depth = svn_depth_unknown;
8892           else
8893             {
8894               child->depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
8895                                                            svn_depth_unknown);
8896               if (new_child)
8897                 SVN_ERR(is_wclocked(&child->locked, wcroot, child_relpath,
8898                                     scratch_pool));
8899             }
8900
8901           child->recorded_time = svn_sqlite__column_int64(stmt, 13);
8902           child->recorded_size = get_recorded_size(stmt, 7);
8903           child->has_checksum = !svn_sqlite__column_is_null(stmt, 6);
8904           child->copied = op_depth > 0 && !svn_sqlite__column_is_null(stmt, 2);
8905           child->had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
8906 #ifdef HAVE_SYMLINK
8907           if (child->had_props)
8908             {
8909               apr_hash_t *properties;
8910               err = svn_sqlite__column_properties(&properties, stmt, 14,
8911                                                   scratch_pool, scratch_pool);
8912               if (err)
8913                 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
8914
8915               child->special = (child->had_props
8916                                 && svn_hash_gets(properties, SVN_PROP_SPECIAL));
8917             }
8918 #endif
8919           if (op_depth == 0)
8920             child->op_root = FALSE;
8921           else
8922             child->op_root = (op_depth == relpath_depth(child_relpath));
8923
8924           if (op_depth && child->op_root)
8925             child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
8926
8927           if (new_child)
8928             svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
8929         }
8930
8931       if (op_depth == 0)
8932         {
8933           child_item->info.have_base = TRUE;
8934
8935           /* Get the lock info, available only at op_depth 0. */
8936           child_item->info.lock = lock_from_columns(stmt, 15, 16, 17, 18,
8937                                                     result_pool);
8938
8939           /* FILE_EXTERNAL flag only on op_depth 0. */
8940           child_item->info.file_external = svn_sqlite__column_boolean(stmt,
8941                                                                       22);
8942         }
8943       else
8944         {
8945           const char *moved_to_relpath;
8946
8947           child_item->nr_layers++;
8948           child_item->info.have_more_work = (child_item->nr_layers > 1);
8949
8950
8951           /* A local_relpath can be moved multiple times at different op
8952              depths and it really depends on the caller what is interesting.
8953              We provide a simple linked list with the moved_from information */
8954
8955           moved_to_relpath = svn_sqlite__column_text(stmt, 21, NULL);
8956           if (moved_to_relpath)
8957             {
8958               struct svn_wc__db_moved_to_info_t *moved_to;
8959               struct svn_wc__db_moved_to_info_t **next;
8960               const char *shadow_op_relpath;
8961               int cur_op_depth;
8962
8963               moved_to = apr_pcalloc(result_pool, sizeof(*moved_to));
8964               moved_to->moved_to_abspath = svn_dirent_join(wcroot->abspath,
8965                                                            moved_to_relpath,
8966                                                            result_pool);
8967
8968               cur_op_depth = relpath_depth(child_relpath);
8969               shadow_op_relpath = child_relpath;
8970
8971               while (cur_op_depth > op_depth)
8972                 {
8973                   shadow_op_relpath = svn_relpath_dirname(shadow_op_relpath,
8974                                                           scratch_pool);
8975                   cur_op_depth--;
8976                 }
8977
8978               moved_to->shadow_op_root_abspath =
8979                         svn_dirent_join(wcroot->abspath, shadow_op_relpath,
8980                                         result_pool);
8981
8982               next = &child_item->info.moved_to;
8983
8984               while (*next &&
8985                      0 < strcmp((*next)->shadow_op_root_abspath,
8986                                 moved_to->shadow_op_root_abspath))
8987                 next = &((*next)->next);
8988
8989               moved_to->next = *next;
8990               *next = moved_to;
8991             }
8992         }
8993
8994       SVN_ERR(svn_sqlite__step(&have_row, stmt));
8995     }
8996
8997   SVN_ERR(svn_sqlite__reset(stmt));
8998
8999   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9000                                     STMT_SELECT_ACTUAL_CHILDREN_INFO));
9001   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9002   SVN_ERR(svn_sqlite__step(&have_row, stmt));
9003
9004   while (have_row)
9005     {
9006       struct read_children_info_item_t *child_item;
9007       struct svn_wc__db_info_t *child;
9008       const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9009       const char *name = svn_relpath_basename(child_relpath, NULL);
9010
9011       child_item = svn_hash_gets(nodes, name);
9012       if (!child_item)
9013         {
9014           child_item = apr_pcalloc(result_pool, sizeof(*child_item));
9015           child_item->info.status = svn_wc__db_status_not_present;
9016         }
9017
9018       child = &child_item->info;
9019
9020       child->changelist = svn_sqlite__column_text(stmt, 1, result_pool);
9021
9022       child->props_mod = !svn_sqlite__column_is_null(stmt, 2);
9023 #ifdef HAVE_SYMLINK
9024       if (child->props_mod)
9025         {
9026           svn_error_t *err;
9027           apr_hash_t *properties;
9028
9029           err = svn_sqlite__column_properties(&properties, stmt, 2,
9030                                               scratch_pool, scratch_pool);
9031           if (err)
9032             SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9033           child->special = (NULL != svn_hash_gets(properties,
9034                                                   SVN_PROP_SPECIAL));
9035         }
9036 #endif
9037
9038       child->conflicted = !svn_sqlite__column_is_null(stmt, 3); /* conflict */
9039
9040       if (child->conflicted)
9041         svn_hash_sets(conflicts, apr_pstrdup(result_pool, name), "");
9042
9043       SVN_ERR(svn_sqlite__step(&have_row, stmt));
9044     }
9045
9046   SVN_ERR(svn_sqlite__reset(stmt));
9047
9048   return SVN_NO_ERROR;
9049 }
9050
9051 svn_error_t *
9052 svn_wc__db_read_children_info(apr_hash_t **nodes,
9053                               apr_hash_t **conflicts,
9054                               svn_wc__db_t *db,
9055                               const char *dir_abspath,
9056                               apr_pool_t *result_pool,
9057                               apr_pool_t *scratch_pool)
9058 {
9059   svn_wc__db_wcroot_t *wcroot;
9060   const char *dir_relpath;
9061
9062   *conflicts = apr_hash_make(result_pool);
9063   *nodes = apr_hash_make(result_pool);
9064   SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9065
9066   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9067                                                 dir_abspath,
9068                                                 scratch_pool, scratch_pool));
9069   VERIFY_USABLE_WCROOT(wcroot);
9070
9071   SVN_WC__DB_WITH_TXN(
9072     read_children_info(wcroot, dir_relpath, *conflicts, *nodes,
9073                        result_pool, scratch_pool),
9074     wcroot);
9075
9076   return SVN_NO_ERROR;
9077 }
9078
9079 static svn_error_t *
9080 db_read_props(apr_hash_t **props,
9081               svn_wc__db_wcroot_t *wcroot,
9082               const char *local_relpath,
9083               apr_pool_t *result_pool,
9084               apr_pool_t *scratch_pool);
9085
9086 static svn_error_t *
9087 read_single_info(const struct svn_wc__db_info_t **info,
9088                  svn_wc__db_wcroot_t *wcroot,
9089                  const char *local_relpath,
9090                  apr_pool_t *result_pool,
9091                  apr_pool_t *scratch_pool)
9092 {
9093   struct svn_wc__db_info_t *mtb;
9094   apr_int64_t repos_id;
9095   const svn_checksum_t *checksum;
9096   const char *original_repos_relpath;
9097   svn_boolean_t have_work;
9098
9099   mtb = apr_pcalloc(result_pool, sizeof(*mtb));
9100
9101   SVN_ERR(read_info(&mtb->status, &mtb->kind, &mtb->revnum,
9102                     &mtb->repos_relpath, &repos_id, &mtb->changed_rev,
9103                     &mtb->changed_date, &mtb->changed_author, &mtb->depth,
9104                     &checksum, NULL, &original_repos_relpath, NULL, NULL,
9105                     &mtb->lock, &mtb->recorded_size, &mtb->recorded_time,
9106                     &mtb->changelist, &mtb->conflicted, &mtb->op_root,
9107                     &mtb->had_props, &mtb->props_mod, &mtb->have_base,
9108                     &mtb->have_more_work, &have_work,
9109                     wcroot, local_relpath,
9110                     result_pool, scratch_pool));
9111
9112   /* Query the same rows in the database again for move information */
9113   if (have_work && (mtb->have_base || mtb->have_more_work))
9114     {
9115       svn_sqlite__stmt_t *stmt;
9116       svn_boolean_t have_row;
9117       const char *cur_relpath = NULL;
9118       int cur_op_depth;
9119
9120       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9121                                         STMT_SELECT_MOVED_TO_NODE));
9122       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9123
9124       SVN_ERR(svn_sqlite__step(&have_row, stmt));
9125
9126       while (have_row)
9127         {
9128           struct svn_wc__db_moved_to_info_t *move;
9129           int op_depth = svn_sqlite__column_int(stmt, 0);
9130           const char *moved_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
9131
9132           move = apr_pcalloc(result_pool, sizeof(*move));
9133           move->moved_to_abspath = svn_dirent_join(wcroot->abspath,
9134                                                    moved_to_relpath,
9135                                                    result_pool);
9136
9137           if (!cur_relpath)
9138             {
9139               cur_relpath = local_relpath;
9140               cur_op_depth = relpath_depth(cur_relpath);
9141             }
9142           while (cur_op_depth > op_depth)
9143             {
9144               cur_relpath = svn_relpath_dirname(cur_relpath, scratch_pool);
9145               cur_op_depth--;
9146             }
9147           move->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath,
9148                                                          cur_relpath,
9149                                                          result_pool);
9150
9151           move->next = mtb->moved_to;
9152           mtb->moved_to = move;
9153
9154           SVN_ERR(svn_sqlite__step(&have_row, stmt));
9155         }
9156
9157       SVN_ERR(svn_sqlite__reset(stmt));
9158     }
9159
9160   /* Maybe we have to get some shadowed lock from BASE to make our test suite
9161      happy... (It might be completely unrelated, but...)
9162      This queries the same BASE row again, joined to the lock table */
9163   if (mtb->have_base && (have_work || mtb->kind == svn_node_file))
9164     {
9165       svn_boolean_t update_root;
9166       svn_wc__db_lock_t **lock_arg = NULL;
9167
9168       if (have_work)
9169         lock_arg = &mtb->lock;
9170
9171       SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL, NULL,
9172                                                 NULL, NULL, NULL, NULL, NULL,
9173                                                 NULL, lock_arg, NULL, NULL,
9174                                                 &update_root,
9175                                                 wcroot, local_relpath,
9176                                                 result_pool, scratch_pool));
9177
9178       mtb->file_external = (update_root && mtb->kind == svn_node_file);
9179     }
9180
9181   if (mtb->status == svn_wc__db_status_added)
9182     {
9183       svn_wc__db_status_t status;
9184
9185       SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9186                             NULL, NULL,
9187                             wcroot, local_relpath,
9188                             result_pool, scratch_pool));
9189
9190       mtb->moved_here = (status == svn_wc__db_status_moved_here);
9191       mtb->incomplete = (status == svn_wc__db_status_incomplete);
9192     }
9193
9194 #ifdef HAVE_SYMLINK
9195   if (mtb->kind == svn_node_file
9196       && (mtb->had_props || mtb->props_mod))
9197     {
9198       apr_hash_t *properties;
9199
9200       if (mtb->props_mod)
9201         SVN_ERR(db_read_props(&properties,
9202                               wcroot, local_relpath,
9203                               scratch_pool, scratch_pool));
9204       else
9205         SVN_ERR(db_read_pristine_props(&properties, wcroot, local_relpath,
9206                                        TRUE /* deleted_ok */,
9207                                        scratch_pool, scratch_pool));
9208
9209       mtb->special = (NULL != svn_hash_gets(properties, SVN_PROP_SPECIAL));
9210     }
9211 #endif
9212
9213   mtb->has_checksum = (checksum != NULL);
9214   mtb->copied = (original_repos_relpath != NULL);
9215
9216   SVN_ERR(svn_wc__db_fetch_repos_info(&mtb->repos_root_url, &mtb->repos_uuid,
9217                                       wcroot->sdb, repos_id, result_pool));
9218
9219   if (mtb->kind == svn_node_dir)
9220     SVN_ERR(is_wclocked(&mtb->locked, wcroot, local_relpath, scratch_pool));
9221
9222   *info = mtb;
9223
9224   return SVN_NO_ERROR;
9225 }
9226
9227 svn_error_t *
9228 svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info,
9229                             svn_wc__db_t *db,
9230                             const char *local_abspath,
9231                             apr_pool_t *result_pool,
9232                             apr_pool_t *scratch_pool)
9233 {
9234   svn_wc__db_wcroot_t *wcroot;
9235   const char *local_relpath;
9236
9237   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9238
9239   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9240                                                 local_abspath,
9241                                                 scratch_pool, scratch_pool));
9242   VERIFY_USABLE_WCROOT(wcroot);
9243
9244   SVN_WC__DB_WITH_TXN(read_single_info(info, wcroot, local_relpath,
9245                                        result_pool, scratch_pool),
9246                       wcroot);
9247
9248   return SVN_NO_ERROR;
9249 }
9250
9251 svn_error_t *
9252 svn_wc__db_read_pristine_info(svn_wc__db_status_t *status,
9253                               svn_node_kind_t *kind,
9254                               svn_revnum_t *changed_rev,
9255                               apr_time_t *changed_date,
9256                               const char **changed_author,
9257                               svn_depth_t *depth,  /* dirs only */
9258                               const svn_checksum_t **checksum, /* files only */
9259                               const char **target, /* symlinks only */
9260                               svn_boolean_t *had_props,
9261                               apr_hash_t **props,
9262                               svn_wc__db_t *db,
9263                               const char *local_abspath,
9264                               apr_pool_t *result_pool,
9265                               apr_pool_t *scratch_pool)
9266 {
9267   svn_wc__db_wcroot_t *wcroot;
9268   const char *local_relpath;
9269   svn_sqlite__stmt_t *stmt;
9270   svn_boolean_t have_row;
9271   svn_error_t *err = NULL;
9272   int op_depth;
9273   svn_wc__db_status_t raw_status;
9274   svn_node_kind_t node_kind;
9275
9276   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9277
9278   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9279                                                 local_abspath,
9280                                                 scratch_pool, scratch_pool));
9281   VERIFY_USABLE_WCROOT(wcroot);
9282
9283   /* Obtain the most likely to exist record first, to make sure we don't
9284      have to obtain the SQLite read-lock multiple times */
9285   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9286                                     STMT_SELECT_NODE_INFO));
9287   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9288   SVN_ERR(svn_sqlite__step(&have_row, stmt));
9289
9290   if (!have_row)
9291     {
9292       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9293                                svn_sqlite__reset(stmt),
9294                                _("The node '%s' was not found."),
9295                                path_for_error_message(wcroot,
9296                                                       local_relpath,
9297                                                       scratch_pool));
9298     }
9299
9300   op_depth = svn_sqlite__column_int(stmt, 0);
9301   raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9302
9303   if (op_depth > 0 && raw_status == svn_wc__db_status_base_deleted)
9304     {
9305       SVN_ERR(svn_sqlite__step_row(stmt));
9306
9307       op_depth = svn_sqlite__column_int(stmt, 0);
9308       raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9309     }
9310
9311   node_kind = svn_sqlite__column_token(stmt, 4, kind_map);
9312
9313   if (status)
9314     {
9315       if (op_depth > 0)
9316         {
9317           err = svn_error_compose_create(err,
9318                                          convert_to_working_status(
9319                                                     status,
9320                                                     raw_status));
9321         }
9322       else
9323         *status = raw_status;
9324     }
9325   if (kind)
9326     {
9327       *kind = node_kind;
9328     }
9329   if (changed_rev)
9330     {
9331       *changed_rev = svn_sqlite__column_revnum(stmt, 8);
9332     }
9333   if (changed_date)
9334     {
9335       *changed_date = svn_sqlite__column_int64(stmt, 9);
9336     }
9337   if (changed_author)
9338     {
9339       *changed_author = svn_sqlite__column_text(stmt, 10,
9340                                                 result_pool);
9341     }
9342   if (depth)
9343     {
9344       if (node_kind != svn_node_dir)
9345         {
9346           *depth = svn_depth_unknown;
9347         }
9348       else
9349         {
9350           *depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
9351                                                  svn_depth_unknown);
9352         }
9353     }
9354   if (checksum)
9355     {
9356       if (node_kind != svn_node_file)
9357         {
9358           *checksum = NULL;
9359         }
9360       else
9361         {
9362           svn_error_t *err2;
9363           err2 = svn_sqlite__column_checksum(checksum, stmt, 6, result_pool);
9364
9365           if (err2 != NULL)
9366             {
9367               if (err)
9368                 err = svn_error_compose_create(
9369                          err,
9370                          svn_error_createf(
9371                                err->apr_err, err2,
9372                               _("The node '%s' has a corrupt checksum value."),
9373                               path_for_error_message(wcroot, local_relpath,
9374                                                      scratch_pool)));
9375               else
9376                 err = err2;
9377             }
9378         }
9379     }
9380   if (target)
9381     {
9382       if (node_kind != svn_node_symlink)
9383         *target = NULL;
9384       else
9385         *target = svn_sqlite__column_text(stmt, 12, result_pool);
9386     }
9387   if (had_props)
9388     {
9389       *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
9390     }
9391   if (props)
9392     {
9393       if (raw_status == svn_wc__db_status_normal
9394           || raw_status == svn_wc__db_status_incomplete)
9395         {
9396           SVN_ERR(svn_sqlite__column_properties(props, stmt, 14,
9397                                                 result_pool, scratch_pool));
9398           if (*props == NULL)
9399             *props = apr_hash_make(result_pool);
9400         }
9401       else
9402         {
9403           assert(svn_sqlite__column_is_null(stmt, 14));
9404           *props = NULL;
9405         }
9406     }
9407
9408   return svn_error_trace(
9409             svn_error_compose_create(err,
9410                                      svn_sqlite__reset(stmt)));
9411 }
9412
9413 svn_error_t *
9414 svn_wc__db_read_children_walker_info(apr_hash_t **nodes,
9415                                      svn_wc__db_t *db,
9416                                      const char *dir_abspath,
9417                                      apr_pool_t *result_pool,
9418                                      apr_pool_t *scratch_pool)
9419 {
9420   svn_wc__db_wcroot_t *wcroot;
9421   const char *dir_relpath;
9422   svn_sqlite__stmt_t *stmt;
9423   svn_boolean_t have_row;
9424
9425   SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9426
9427   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9428                                              dir_abspath,
9429                                              scratch_pool, scratch_pool));
9430   VERIFY_USABLE_WCROOT(wcroot);
9431
9432   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9433                                     STMT_SELECT_NODE_CHILDREN_WALKER_INFO));
9434   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9435   SVN_ERR(svn_sqlite__step(&have_row, stmt));
9436
9437   *nodes = apr_hash_make(result_pool);
9438   while (have_row)
9439     {
9440       struct svn_wc__db_walker_info_t *child;
9441       const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9442       const char *name = svn_relpath_basename(child_relpath, NULL);
9443       int op_depth = svn_sqlite__column_int(stmt, 1);
9444       svn_error_t *err;
9445
9446       child = apr_palloc(result_pool, sizeof(*child));
9447       child->status = svn_sqlite__column_token(stmt, 2, presence_map);
9448       if (op_depth > 0)
9449         {
9450           err = convert_to_working_status(&child->status, child->status);
9451           if (err)
9452             SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9453         }
9454       child->kind = svn_sqlite__column_token(stmt, 3, kind_map);
9455       svn_hash_sets(*nodes, apr_pstrdup(result_pool, name), child);
9456
9457       SVN_ERR(svn_sqlite__step(&have_row, stmt));
9458     }
9459
9460   SVN_ERR(svn_sqlite__reset(stmt));
9461
9462   return SVN_NO_ERROR;
9463 }
9464
9465 svn_error_t *
9466 svn_wc__db_read_node_install_info(const char **wcroot_abspath,
9467                                   const svn_checksum_t **sha1_checksum,
9468                                   apr_hash_t **pristine_props,
9469                                   apr_time_t *changed_date,
9470                                   svn_wc__db_t *db,
9471                                   const char *local_abspath,
9472                                   const char *wri_abspath,
9473                                   apr_pool_t *result_pool,
9474                                   apr_pool_t *scratch_pool)
9475 {
9476   svn_wc__db_wcroot_t *wcroot;
9477   const char *local_relpath;
9478   svn_sqlite__stmt_t *stmt;
9479   svn_error_t *err = NULL;
9480   svn_boolean_t have_row;
9481
9482   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9483
9484   if (!wri_abspath)
9485     wri_abspath = local_abspath;
9486
9487   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9488                               wri_abspath, scratch_pool, scratch_pool));
9489   VERIFY_USABLE_WCROOT(wcroot);
9490
9491   if (local_abspath != wri_abspath
9492       && strcmp(local_abspath, wri_abspath))
9493     {
9494       if (!svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
9495         return svn_error_createf(
9496                     SVN_ERR_WC_PATH_NOT_FOUND, NULL,
9497                     _("The node '%s' is not in working copy '%s'"),
9498                     svn_dirent_local_style(local_abspath, scratch_pool),
9499                     svn_dirent_local_style(wcroot->abspath, scratch_pool));
9500
9501       local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
9502     }
9503
9504   if (wcroot_abspath != NULL)
9505     *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
9506
9507   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9508                                     STMT_SELECT_NODE_INFO));
9509
9510   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9511
9512   SVN_ERR(svn_sqlite__step(&have_row, stmt));
9513
9514   if (have_row)
9515     {
9516       if (!err && sha1_checksum)
9517         err = svn_sqlite__column_checksum(sha1_checksum, stmt, 6, result_pool);
9518
9519       if (!err && pristine_props)
9520         {
9521           err = svn_sqlite__column_properties(pristine_props, stmt, 14,
9522                                               result_pool, scratch_pool);
9523           /* Null means no props (assuming presence normal or incomplete). */
9524           if (*pristine_props == NULL)
9525             *pristine_props = apr_hash_make(result_pool);
9526         }
9527
9528       if (changed_date)
9529         *changed_date = svn_sqlite__column_int64(stmt, 9);
9530     }
9531   else
9532     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9533                              svn_sqlite__reset(stmt),
9534                              _("The node '%s' is not installable"),
9535                              svn_dirent_local_style(local_abspath,
9536                                                     scratch_pool));
9537
9538   SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9539
9540   return SVN_NO_ERROR;
9541 }
9542
9543
9544
9545 /* The body of svn_wc__db_read_url().
9546  */
9547 static svn_error_t *
9548 read_url_txn(const char **url,
9549              svn_wc__db_wcroot_t *wcroot,
9550              const char *local_relpath,
9551              apr_pool_t *result_pool,
9552              apr_pool_t *scratch_pool)
9553 {
9554   svn_wc__db_status_t status;
9555   const char *repos_relpath;
9556   const char *repos_root_url;
9557   apr_int64_t repos_id;
9558   svn_boolean_t have_base;
9559
9560   SVN_ERR(read_info(&status, NULL, NULL, &repos_relpath, &repos_id, NULL,
9561                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9562                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9563                     &have_base, NULL, NULL,
9564                     wcroot, local_relpath, scratch_pool, scratch_pool));
9565
9566   if (repos_relpath == NULL)
9567     {
9568       if (status == svn_wc__db_status_added)
9569         {
9570           SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id, NULL,
9571                                 NULL, NULL, NULL, NULL, NULL,
9572                                 wcroot, local_relpath,
9573                                 scratch_pool, scratch_pool));
9574         }
9575       else if (status == svn_wc__db_status_deleted)
9576         {
9577           const char *base_del_relpath;
9578           const char *work_del_relpath;
9579
9580           SVN_ERR(scan_deletion_txn(&base_del_relpath, NULL,
9581                                     &work_del_relpath,
9582                                     NULL, wcroot,
9583                                     local_relpath,
9584                                     scratch_pool,
9585                                     scratch_pool));
9586
9587           if (base_del_relpath)
9588             {
9589               SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
9590                                                         &repos_relpath,
9591                                                         &repos_id,
9592                                                         NULL, NULL, NULL,
9593                                                         NULL, NULL, NULL,
9594                                                         NULL, NULL, NULL, NULL,
9595                                                         wcroot,
9596                                                         base_del_relpath,
9597                                                         scratch_pool,
9598                                                         scratch_pool));
9599
9600               repos_relpath = svn_relpath_join(
9601                                     repos_relpath,
9602                                     svn_dirent_skip_ancestor(base_del_relpath,
9603                                                              local_relpath),
9604                                     scratch_pool);
9605             }
9606           else
9607             {
9608               /* The parent of the WORKING delete, must be an addition */
9609               const char *work_relpath = NULL;
9610
9611               /* work_del_relpath should not be NULL. However, we have
9612                * observed instances where that assumption was not met.
9613                * Bail out in that case instead of crashing with a segfault.
9614                */
9615               SVN_ERR_ASSERT(work_del_relpath != NULL);
9616               work_relpath = svn_relpath_dirname(work_del_relpath,
9617                                                  scratch_pool);
9618
9619               SVN_ERR(scan_addition(NULL, NULL, &repos_relpath, &repos_id,
9620                                     NULL, NULL, NULL, NULL, NULL, NULL,
9621                                     wcroot, work_relpath,
9622                                     scratch_pool, scratch_pool));
9623
9624               repos_relpath = svn_relpath_join(
9625                                     repos_relpath,
9626                                     svn_dirent_skip_ancestor(work_relpath,
9627                                                              local_relpath),
9628                                     scratch_pool);
9629             }
9630         }
9631       else if (status == svn_wc__db_status_excluded)
9632         {
9633           const char *parent_relpath;
9634           const char *name;
9635           const char *url2;
9636
9637           /* Set 'url' to the *full URL* of the parent WC dir,
9638            * and 'name' to the *single path component* that is the
9639            * basename of this WC directory, so that joining them will result
9640            * in the correct full URL. */
9641           svn_relpath_split(&parent_relpath, &name, local_relpath,
9642                             scratch_pool);
9643           SVN_ERR(read_url_txn(&url2, wcroot, parent_relpath,
9644                                scratch_pool, scratch_pool));
9645
9646           *url = svn_path_url_add_component2(url2, name, result_pool);
9647
9648           return SVN_NO_ERROR;
9649         }
9650       else
9651         {
9652           /* All working statee are explicitly handled and all base statee
9653              have a repos_relpath */
9654           SVN_ERR_MALFUNCTION();
9655         }
9656     }
9657
9658   SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot->sdb,
9659                                       repos_id, scratch_pool));
9660
9661   SVN_ERR_ASSERT(repos_root_url != NULL && repos_relpath != NULL);
9662   *url = svn_path_url_add_component2(repos_root_url, repos_relpath,
9663                                      result_pool);
9664
9665   return SVN_NO_ERROR;
9666 }
9667
9668
9669 svn_error_t *
9670 svn_wc__db_read_url(const char **url,
9671                     svn_wc__db_t *db,
9672                     const char *local_abspath,
9673                     apr_pool_t *result_pool,
9674                     apr_pool_t *scratch_pool)
9675 {
9676   svn_wc__db_wcroot_t *wcroot;
9677   const char *local_relpath;
9678
9679   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9680
9681   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9682                                                 local_abspath,
9683                                                 scratch_pool, scratch_pool));
9684   VERIFY_USABLE_WCROOT(wcroot);
9685
9686   SVN_WC__DB_WITH_TXN(read_url_txn(url, wcroot, local_relpath,
9687                                    result_pool, scratch_pool),
9688                       wcroot);
9689
9690   return SVN_NO_ERROR;
9691 }
9692
9693
9694 /* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
9695    a hash table mapping <tt>char *</tt> names onto svn_string_t *
9696    values for any properties of immediate or recursive child nodes of
9697    LOCAL_ABSPATH, the actual query being determined by STMT_IDX.
9698    If FILES_ONLY is true, only report properties for file child nodes.
9699    Check for cancellation between calls of RECEIVER_FUNC.
9700 */
9701 typedef struct cache_props_baton_t
9702 {
9703   svn_depth_t depth;
9704   svn_boolean_t pristine;
9705   const apr_array_header_t *changelists;
9706   svn_cancel_func_t cancel_func;
9707   void *cancel_baton;
9708 } cache_props_baton_t;
9709
9710
9711 static svn_error_t *
9712 cache_props_recursive(void *cb_baton,
9713                       svn_wc__db_wcroot_t *wcroot,
9714                       const char *local_relpath,
9715                       apr_pool_t *scratch_pool)
9716 {
9717   cache_props_baton_t *baton = cb_baton;
9718   svn_sqlite__stmt_t *stmt;
9719   int stmt_idx;
9720
9721   SVN_ERR(populate_targets_tree(wcroot, local_relpath, baton->depth,
9722                                 baton->changelists, scratch_pool));
9723
9724   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
9725                                       STMT_CREATE_TARGET_PROP_CACHE));
9726
9727   if (baton->pristine)
9728     stmt_idx = STMT_CACHE_TARGET_PRISTINE_PROPS;
9729   else
9730     stmt_idx = STMT_CACHE_TARGET_PROPS;
9731
9732   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
9733   SVN_ERR(svn_sqlite__bind_int64(stmt, 1, wcroot->wc_id));
9734   SVN_ERR(svn_sqlite__step_done(stmt));
9735
9736   return SVN_NO_ERROR;
9737 }
9738
9739
9740 svn_error_t *
9741 svn_wc__db_read_props_streamily(svn_wc__db_t *db,
9742                                 const char *local_abspath,
9743                                 svn_depth_t depth,
9744                                 svn_boolean_t pristine,
9745                                 const apr_array_header_t *changelists,
9746                                 svn_wc__proplist_receiver_t receiver_func,
9747                                 void *receiver_baton,
9748                                 svn_cancel_func_t cancel_func,
9749                                 void *cancel_baton,
9750                                 apr_pool_t *scratch_pool)
9751 {
9752   svn_wc__db_wcroot_t *wcroot;
9753   const char *local_relpath;
9754   svn_sqlite__stmt_t *stmt;
9755   cache_props_baton_t baton;
9756   svn_boolean_t have_row;
9757   apr_pool_t *iterpool;
9758   svn_error_t *err = NULL;
9759
9760   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9761   SVN_ERR_ASSERT(receiver_func);
9762   SVN_ERR_ASSERT((depth == svn_depth_files) ||
9763                  (depth == svn_depth_immediates) ||
9764                  (depth == svn_depth_infinity));
9765
9766   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
9767                                                 db, local_abspath,
9768                                                 scratch_pool, scratch_pool));
9769   VERIFY_USABLE_WCROOT(wcroot);
9770
9771   baton.depth = depth;
9772   baton.pristine = pristine;
9773   baton.changelists = changelists;
9774   baton.cancel_func = cancel_func;
9775   baton.cancel_baton = cancel_baton;
9776
9777   SVN_ERR(with_finalization(wcroot, local_relpath,
9778                             cache_props_recursive, &baton,
9779                             NULL, NULL,
9780                             cancel_func, cancel_baton,
9781                             NULL, NULL,
9782                             STMT_DROP_TARGETS_LIST,
9783                             scratch_pool));
9784
9785   iterpool = svn_pool_create(scratch_pool);
9786
9787   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9788                                     STMT_SELECT_ALL_TARGET_PROP_CACHE));
9789   SVN_ERR(svn_sqlite__step(&have_row, stmt));
9790   while (!err && have_row)
9791     {
9792       apr_hash_t *props;
9793
9794       svn_pool_clear(iterpool);
9795
9796       SVN_ERR(svn_sqlite__column_properties(&props, stmt, 1, iterpool,
9797                                             iterpool));
9798
9799       /* see if someone wants to cancel this operation. */
9800       if (cancel_func)
9801         err = cancel_func(cancel_baton);
9802
9803       if (!err && props && apr_hash_count(props) != 0)
9804         {
9805           const char *child_relpath;
9806           const char *child_abspath;
9807
9808           child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9809           child_abspath = svn_dirent_join(wcroot->abspath,
9810                                           child_relpath, iterpool);
9811
9812           err = receiver_func(receiver_baton, child_abspath, props, iterpool);
9813         }
9814
9815       err = svn_error_compose_create(err, svn_sqlite__step(&have_row, stmt));
9816     }
9817
9818   err = svn_error_compose_create(err, svn_sqlite__reset(stmt));
9819
9820   svn_pool_destroy(iterpool);
9821
9822   SVN_ERR(svn_error_compose_create(
9823                     err,
9824                     svn_sqlite__exec_statements(wcroot->sdb,
9825                                                 STMT_DROP_TARGET_PROP_CACHE)));
9826   return SVN_NO_ERROR;
9827 }
9828
9829
9830 /* Helper for svn_wc__db_read_props().
9831  */
9832 static svn_error_t *
9833 db_read_props(apr_hash_t **props,
9834               svn_wc__db_wcroot_t *wcroot,
9835               const char *local_relpath,
9836               apr_pool_t *result_pool,
9837               apr_pool_t *scratch_pool)
9838 {
9839   svn_sqlite__stmt_t *stmt;
9840   svn_boolean_t have_row;
9841   svn_error_t *err = NULL;
9842
9843   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9844                                     STMT_SELECT_ACTUAL_PROPS));
9845   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9846   SVN_ERR(svn_sqlite__step(&have_row, stmt));
9847
9848   if (have_row && !svn_sqlite__column_is_null(stmt, 0))
9849     {
9850       err = svn_sqlite__column_properties(props, stmt, 0,
9851                                           result_pool, scratch_pool);
9852     }
9853   else
9854     have_row = FALSE;
9855
9856   SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9857
9858   if (have_row)
9859     return SVN_NO_ERROR;
9860
9861   /* No local changes. Return the pristine props for this node.  */
9862   SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, FALSE,
9863                                  result_pool, scratch_pool));
9864   if (*props == NULL)
9865     {
9866       /* Pristine properties are not defined for this node.
9867          ### we need to determine whether this node is in a state that
9868          ### allows for ACTUAL properties (ie. not deleted). for now,
9869          ### just say all nodes, no matter the state, have at least an
9870          ### empty set of props.  */
9871       *props = apr_hash_make(result_pool);
9872     }
9873
9874   return SVN_NO_ERROR;
9875 }
9876
9877
9878 svn_error_t *
9879 svn_wc__db_read_props(apr_hash_t **props,
9880                       svn_wc__db_t *db,
9881                       const char *local_abspath,
9882                       apr_pool_t *result_pool,
9883                       apr_pool_t *scratch_pool)
9884 {
9885   svn_wc__db_wcroot_t *wcroot;
9886   const char *local_relpath;
9887
9888   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9889
9890   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9891                               local_abspath, scratch_pool, scratch_pool));
9892   VERIFY_USABLE_WCROOT(wcroot);
9893
9894   SVN_WC__DB_WITH_TXN(db_read_props(props, wcroot, local_relpath,
9895                                     result_pool, scratch_pool),
9896                       wcroot);
9897
9898   return SVN_NO_ERROR;
9899 }
9900
9901
9902 static svn_error_t *
9903 db_read_pristine_props(apr_hash_t **props,
9904                        svn_wc__db_wcroot_t *wcroot,
9905                        const char *local_relpath,
9906                        svn_boolean_t deleted_ok,
9907                        apr_pool_t *result_pool,
9908                        apr_pool_t *scratch_pool)
9909 {
9910   svn_sqlite__stmt_t *stmt;
9911   svn_boolean_t have_row;
9912   svn_wc__db_status_t presence;
9913
9914   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_NODE_PROPS));
9915   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9916
9917   SVN_ERR(svn_sqlite__step(&have_row, stmt));
9918
9919   if (!have_row)
9920     {
9921       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9922                                svn_sqlite__reset(stmt),
9923                                _("The node '%s' was not found."),
9924                                path_for_error_message(wcroot,
9925                                                       local_relpath,
9926                                                       scratch_pool));
9927     }
9928
9929
9930   /* Examine the presence: */
9931   presence = svn_sqlite__column_token(stmt, 1, presence_map);
9932
9933   /* For "base-deleted", it is obvious the pristine props are located
9934      below the current node. Fetch the NODE from the next record. */
9935   if (presence == svn_wc__db_status_base_deleted && deleted_ok)
9936     {
9937       SVN_ERR(svn_sqlite__step(&have_row, stmt));
9938
9939       SVN_ERR_ASSERT(have_row);
9940
9941       presence = svn_sqlite__column_token(stmt, 1, presence_map);
9942     }
9943
9944   /* normal or copied: Fetch properties (during update we want
9945      properties for incomplete as well) */
9946   if (presence == svn_wc__db_status_normal
9947       || presence == svn_wc__db_status_incomplete)
9948     {
9949       svn_error_t *err;
9950
9951       err = svn_sqlite__column_properties(props, stmt, 0, result_pool,
9952                                           scratch_pool);
9953       SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9954
9955       if (!*props)
9956         *props = apr_hash_make(result_pool);
9957
9958       return SVN_NO_ERROR;
9959     }
9960
9961   return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
9962                            svn_sqlite__reset(stmt),
9963                            _("The node '%s' has a status that"
9964                              " has no properties."),
9965                            path_for_error_message(wcroot,
9966                                                   local_relpath,
9967                                                   scratch_pool));
9968 }
9969
9970
9971 svn_error_t *
9972 svn_wc__db_read_pristine_props(apr_hash_t **props,
9973                                svn_wc__db_t *db,
9974                                const char *local_abspath,
9975                                apr_pool_t *result_pool,
9976                                apr_pool_t *scratch_pool)
9977 {
9978   svn_wc__db_wcroot_t *wcroot;
9979   const char *local_relpath;
9980
9981   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9982
9983   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9984                               local_abspath, scratch_pool, scratch_pool));
9985   VERIFY_USABLE_WCROOT(wcroot);
9986
9987   SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, TRUE,
9988                                  result_pool, scratch_pool));
9989   return SVN_NO_ERROR;
9990 }
9991
9992 svn_error_t *
9993 svn_wc__db_prop_retrieve_recursive(apr_hash_t **values,
9994                                    svn_wc__db_t *db,
9995                                    const char *local_abspath,
9996                                    const char *propname,
9997                                    apr_pool_t *result_pool,
9998                                    apr_pool_t *scratch_pool)
9999 {
10000   svn_wc__db_wcroot_t *wcroot;
10001   const char *local_relpath;
10002   svn_sqlite__stmt_t *stmt;
10003   svn_boolean_t have_row;
10004   apr_pool_t *iterpool;
10005
10006   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10007
10008   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10009                               local_abspath, scratch_pool, scratch_pool));
10010   VERIFY_USABLE_WCROOT(wcroot);
10011
10012   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10013                                     STMT_SELECT_CURRENT_PROPS_RECURSIVE));
10014   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10015
10016   *values = apr_hash_make(result_pool);
10017
10018   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10019   iterpool = svn_pool_create(scratch_pool);
10020   while (have_row)
10021   {
10022     apr_hash_t *node_props;
10023     svn_string_t *value;
10024
10025     svn_pool_clear(iterpool);
10026
10027     SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 0,
10028                                           iterpool, iterpool));
10029
10030     value = (node_props
10031                 ? svn_hash_gets(node_props, propname)
10032                 : NULL);
10033
10034     if (value)
10035       {
10036         svn_hash_sets(*values,
10037                       svn_dirent_join(wcroot->abspath,
10038                                       svn_sqlite__column_text(stmt, 1, NULL),
10039                                       result_pool),
10040                       svn_string_dup(value, result_pool));
10041       }
10042
10043     SVN_ERR(svn_sqlite__step(&have_row, stmt));
10044   }
10045
10046   svn_pool_destroy(iterpool);
10047
10048   return svn_error_trace(svn_sqlite__reset(stmt));
10049 }
10050
10051 /* The body of svn_wc__db_read_cached_iprops(). */
10052 static svn_error_t *
10053 db_read_cached_iprops(apr_array_header_t **iprops,
10054                       svn_wc__db_wcroot_t *wcroot,
10055                       const char *local_relpath,
10056                       apr_pool_t *result_pool,
10057                       apr_pool_t *scratch_pool)
10058 {
10059   svn_sqlite__stmt_t *stmt;
10060   svn_boolean_t have_row;
10061
10062   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_IPROPS));
10063   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10064   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10065
10066   if (!have_row)
10067     {
10068       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10069                                svn_sqlite__reset(stmt),
10070                                _("The node '%s' was not found."),
10071                                path_for_error_message(wcroot, local_relpath,
10072                                                       scratch_pool));
10073     }
10074
10075   SVN_ERR(svn_sqlite__column_iprops(iprops, stmt, 0,
10076                                     result_pool, scratch_pool));
10077
10078   SVN_ERR(svn_sqlite__reset(stmt));
10079
10080   return SVN_NO_ERROR;
10081 }
10082
10083 svn_error_t *
10084 svn_wc__db_read_cached_iprops(apr_array_header_t **iprops,
10085                               svn_wc__db_t *db,
10086                               const char *local_abspath,
10087                               apr_pool_t *result_pool,
10088                               apr_pool_t *scratch_pool)
10089 {
10090   svn_wc__db_wcroot_t *wcroot;
10091   const char *local_relpath;
10092
10093   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10094
10095   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10096                                                 db, local_abspath,
10097                                                 scratch_pool, scratch_pool));
10098   VERIFY_USABLE_WCROOT(wcroot);
10099
10100   /* Don't use with_txn yet, as we perform just a single transaction */
10101   SVN_ERR(db_read_cached_iprops(iprops, wcroot, local_relpath,
10102                                 result_pool, scratch_pool));
10103
10104   if (!*iprops)
10105     {
10106       *iprops = apr_array_make(result_pool, 0,
10107                                sizeof(svn_prop_inherited_item_t *));
10108     }
10109
10110   return SVN_NO_ERROR;
10111 }
10112
10113 /* Remove all prop name value pairs from PROP_HASH where the property
10114    name is not PROPNAME. */
10115 static void
10116 filter_unwanted_props(apr_hash_t *prop_hash,
10117                       const char * propname,
10118                       apr_pool_t *scratch_pool)
10119 {
10120   apr_hash_index_t *hi;
10121
10122   for (hi = apr_hash_first(scratch_pool, prop_hash);
10123        hi;
10124        hi = apr_hash_next(hi))
10125     {
10126       const char *ipropname = svn__apr_hash_index_key(hi);
10127
10128       if (strcmp(ipropname, propname) != 0)
10129         svn_hash_sets(prop_hash, ipropname, NULL);
10130     }
10131   return;
10132 }
10133
10134 /* Get the changed properties as stored in the ACTUAL table */
10135 static svn_error_t *
10136 db_get_changed_props(apr_hash_t **actual_props,
10137                      svn_wc__db_wcroot_t *wcroot,
10138                      const char *local_relpath,
10139                      apr_pool_t *result_pool,
10140                      apr_pool_t *scratch_pool)
10141 {
10142   svn_sqlite__stmt_t *stmt;
10143   svn_boolean_t have_row;
10144   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10145                                 STMT_SELECT_ACTUAL_PROPS));
10146   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10147   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10148
10149   if (have_row && !svn_sqlite__column_is_null(stmt, 0))
10150     SVN_ERR(svn_sqlite__column_properties(actual_props, stmt, 0,
10151                                           result_pool, scratch_pool));
10152   else
10153     *actual_props = NULL; /* Cached when we read that record */
10154
10155   return svn_error_trace(svn_sqlite__reset(stmt));
10156 }
10157
10158 /* The body of svn_wc__db_read_inherited_props().  */
10159 static svn_error_t *
10160 db_read_inherited_props(apr_array_header_t **inherited_props,
10161                         apr_hash_t **actual_props,
10162                         svn_wc__db_wcroot_t *wcroot,
10163                         const char *local_relpath,
10164                         const char *propname,
10165                         apr_pool_t *result_pool,
10166                         apr_pool_t *scratch_pool)
10167 {
10168   int i;
10169   apr_array_header_t *cached_iprops = NULL;
10170   apr_array_header_t *iprops;
10171   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
10172   svn_sqlite__stmt_t *stmt;
10173   const char *relpath;
10174   const char *expected_parent_repos_relpath = NULL;
10175   const char *parent_relpath;
10176
10177   iprops = apr_array_make(result_pool, 1,
10178                            sizeof(svn_prop_inherited_item_t *));
10179   *inherited_props = iprops;
10180
10181   if (actual_props)
10182     *actual_props = NULL;
10183
10184   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10185                                     STMT_SELECT_NODE_INFO));
10186
10187   relpath = local_relpath;
10188
10189   /* Walk up to the root of the WC looking for inherited properties.  When we
10190      reach the WC root also check for cached inherited properties. */
10191   for (relpath = local_relpath; relpath; relpath = parent_relpath)
10192     {
10193       svn_boolean_t have_row;
10194       int op_depth;
10195       svn_wc__db_status_t status;
10196       apr_hash_t *node_props;
10197
10198       parent_relpath = relpath[0] ? svn_relpath_dirname(relpath, scratch_pool)
10199                                   : NULL;
10200
10201       svn_pool_clear(iterpool);
10202
10203       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, relpath));
10204
10205       SVN_ERR(svn_sqlite__step(&have_row, stmt));
10206
10207       if (!have_row)
10208         return svn_error_createf(
10209                     SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
10210                     _("The node '%s' was not found."),
10211                     path_for_error_message(wcroot, relpath,
10212                                            scratch_pool));
10213
10214       op_depth = svn_sqlite__column_int(stmt, 0);
10215
10216       status = svn_sqlite__column_token(stmt, 3, presence_map);
10217
10218       if (status != svn_wc__db_status_normal
10219           && status != svn_wc__db_status_incomplete)
10220         return svn_error_createf(
10221                     SVN_ERR_WC_PATH_UNEXPECTED_STATUS, svn_sqlite__reset(stmt),
10222                     _("The node '%s' has a status that has no properties."),
10223                     path_for_error_message(wcroot, relpath,
10224                                            scratch_pool));
10225
10226       if (op_depth > 0)
10227         {
10228           /* WORKING node. Nothing to check */
10229         }
10230       else if (expected_parent_repos_relpath)
10231         {
10232           const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10233
10234           if (strcmp(expected_parent_repos_relpath, repos_relpath) != 0)
10235             {
10236               /* The child of this node has a different parent than this node
10237                  (It is "switched"), so we can stop here. Note that switched
10238                  with the same parent is not interesting for us here. */
10239               SVN_ERR(svn_sqlite__reset(stmt));
10240               break;
10241             }
10242
10243           expected_parent_repos_relpath =
10244               svn_relpath_dirname(expected_parent_repos_relpath, scratch_pool);
10245         }
10246       else
10247         {
10248           const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10249
10250           expected_parent_repos_relpath =
10251               svn_relpath_dirname(repos_relpath, scratch_pool);
10252         }
10253
10254       if (op_depth == 0
10255           && !svn_sqlite__column_is_null(stmt, 16))
10256         {
10257           /* The node contains a cache. No reason to look further */
10258           SVN_ERR(svn_sqlite__column_iprops(&cached_iprops, stmt, 16,
10259                                             result_pool, iterpool));
10260
10261           parent_relpath = NULL; /* Stop after this */
10262         }
10263
10264       SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 14,
10265                                             iterpool, iterpool));
10266
10267       SVN_ERR(svn_sqlite__reset(stmt));
10268
10269       /* If PARENT_ABSPATH is a parent of LOCAL_ABSPATH, then LOCAL_ABSPATH
10270          can inherit properties from it. */
10271       if (relpath != local_relpath)
10272         {
10273           apr_hash_t *changed_props;
10274
10275           SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10276                                        result_pool, iterpool));
10277
10278           if (changed_props)
10279             node_props = changed_props;
10280           else if (node_props)
10281             node_props = svn_prop_hash_dup(node_props, result_pool);
10282
10283           if (node_props && apr_hash_count(node_props))
10284             {
10285               /* If we only want PROPNAME filter out any other properties. */
10286               if (propname)
10287                 filter_unwanted_props(node_props, propname, iterpool);
10288
10289               if (apr_hash_count(node_props))
10290                 {
10291                   svn_prop_inherited_item_t *iprop_elt =
10292                     apr_pcalloc(result_pool,
10293                                 sizeof(svn_prop_inherited_item_t));
10294                   iprop_elt->path_or_url = svn_dirent_join(wcroot->abspath,
10295                                                            relpath,
10296                                                            result_pool);
10297
10298                   iprop_elt->prop_hash = node_props;
10299                   /* Build the output array in depth-first order. */
10300                   svn_sort__array_insert(&iprop_elt, iprops, 0);
10301                 }
10302             }
10303         }
10304       else if (actual_props)
10305         {
10306           apr_hash_t *changed_props;
10307
10308           SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10309                                        result_pool, iterpool));
10310
10311           if (changed_props)
10312             *actual_props = changed_props;
10313           else if (node_props)
10314             *actual_props = svn_prop_hash_dup(node_props, result_pool);
10315         }
10316     }
10317
10318   if (cached_iprops)
10319     {
10320       for (i = cached_iprops->nelts - 1; i >= 0; i--)
10321         {
10322           svn_prop_inherited_item_t *cached_iprop =
10323             APR_ARRAY_IDX(cached_iprops, i, svn_prop_inherited_item_t *);
10324
10325           /* An empty property hash in the iprops cache means there are no
10326              inherited properties. */
10327           if (apr_hash_count(cached_iprop->prop_hash) == 0)
10328             continue;
10329
10330           if (propname)
10331             filter_unwanted_props(cached_iprop->prop_hash, propname,
10332                                   scratch_pool);
10333
10334           /* If we didn't filter everything then keep this iprop. */
10335           if (apr_hash_count(cached_iprop->prop_hash))
10336             svn_sort__array_insert(&cached_iprop, iprops, 0);
10337         }
10338     }
10339
10340   if (actual_props && !*actual_props)
10341     *actual_props = apr_hash_make(result_pool);
10342
10343   svn_pool_destroy(iterpool);
10344   return SVN_NO_ERROR;
10345 }
10346
10347 svn_error_t *
10348 svn_wc__db_read_inherited_props(apr_array_header_t **iprops,
10349                                 apr_hash_t **actual_props,
10350                                 svn_wc__db_t *db,
10351                                 const char *local_abspath,
10352                                 const char *propname,
10353                                 apr_pool_t *result_pool,
10354                                 apr_pool_t *scratch_pool)
10355 {
10356   svn_wc__db_wcroot_t *wcroot;
10357   const char *local_relpath;
10358
10359   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10360
10361   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10362                                                 db, local_abspath,
10363                                                 scratch_pool, scratch_pool));
10364   VERIFY_USABLE_WCROOT(wcroot);
10365
10366   SVN_WC__DB_WITH_TXN(db_read_inherited_props(iprops, actual_props,
10367                                               wcroot, local_relpath, propname,
10368                                               result_pool, scratch_pool),
10369                       wcroot);
10370
10371   return SVN_NO_ERROR;
10372 }
10373
10374 /* The body of svn_wc__db_get_children_with_cached_iprops().
10375  */
10376 static svn_error_t *
10377 get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10378                                 svn_wc__db_wcroot_t *wcroot,
10379                                 const char *local_relpath,
10380                                 svn_depth_t depth,
10381                                 apr_pool_t *result_pool,
10382                                 apr_pool_t *scratch_pool)
10383 {
10384   svn_sqlite__stmt_t *stmt;
10385   svn_boolean_t have_row;
10386
10387   *iprop_paths = apr_hash_make(result_pool);
10388
10389   /* First check if LOCAL_RELPATH itself has iprops */
10390   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10391                                     STMT_SELECT_IPROPS_NODE));
10392   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10393   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10394
10395   if (have_row)
10396    {
10397       const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10398                                                                NULL);
10399       const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10400                                                        relpath_with_cache,
10401                                                        result_pool);
10402       svn_hash_sets(*iprop_paths, abspath_with_cache,
10403                     svn_sqlite__column_text(stmt, 1, result_pool));
10404     }
10405   SVN_ERR(svn_sqlite__reset(stmt));
10406
10407   if (depth == svn_depth_empty)
10408     return SVN_NO_ERROR;
10409
10410   /* Now fetch information for children or all descendants */
10411   if (depth == svn_depth_files
10412       || depth == svn_depth_immediates)
10413     {
10414       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10415                                         STMT_SELECT_IPROPS_CHILDREN));
10416     }
10417   else /* Default to svn_depth_infinity. */
10418     {
10419       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10420                                         STMT_SELECT_IPROPS_RECURSIVE));
10421     }
10422
10423   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10424   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10425
10426   while (have_row)
10427     {
10428       const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10429                                                                NULL);
10430       const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10431                                                        relpath_with_cache,
10432                                                        result_pool);
10433       svn_hash_sets(*iprop_paths, abspath_with_cache,
10434                     svn_sqlite__column_text(stmt, 1, result_pool));
10435       SVN_ERR(svn_sqlite__step(&have_row, stmt));
10436     }
10437
10438   SVN_ERR(svn_sqlite__reset(stmt));
10439
10440   /* For depth files we should filter non files */
10441   if (depth == svn_depth_files)
10442     {
10443       apr_hash_index_t *hi;
10444       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
10445
10446       for (hi = apr_hash_first(scratch_pool, *iprop_paths);
10447            hi;
10448            hi = apr_hash_next(hi))
10449         {
10450           const char *child_abspath = svn__apr_hash_index_key(hi);
10451           const char *child_relpath;
10452           svn_node_kind_t child_kind;
10453
10454           svn_pool_clear(iterpool);
10455
10456           child_relpath = svn_dirent_is_child(local_relpath, child_abspath,
10457                                               NULL);
10458
10459           if (! child_relpath)
10460             {
10461               continue; /* local_relpath itself */
10462             }
10463
10464           SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &child_kind, NULL,
10465                                                     NULL, NULL, NULL, NULL,
10466                                                     NULL, NULL, NULL, NULL,
10467                                                     NULL, NULL, NULL, NULL,
10468                                                     wcroot, child_relpath,
10469                                                     scratch_pool,
10470                                                     scratch_pool));
10471
10472           /* Filter if not a file */
10473           if (child_kind != svn_node_file)
10474             {
10475               svn_hash_sets(*iprop_paths, child_abspath, NULL);
10476             }
10477         }
10478
10479       svn_pool_destroy(iterpool);
10480     }
10481
10482   return SVN_NO_ERROR;
10483 }
10484
10485 svn_error_t *
10486 svn_wc__db_get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10487                                            svn_depth_t depth,
10488                                            const char *local_abspath,
10489                                            svn_wc__db_t *db,
10490                                            apr_pool_t *result_pool,
10491                                            apr_pool_t *scratch_pool)
10492 {
10493   svn_wc__db_wcroot_t *wcroot;
10494   const char *local_relpath;
10495
10496   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10497
10498   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10499                                                 local_abspath, scratch_pool,
10500                                                 scratch_pool));
10501   VERIFY_USABLE_WCROOT(wcroot);
10502
10503   SVN_WC__DB_WITH_TXN(
10504     get_children_with_cached_iprops(iprop_paths, wcroot, local_relpath,
10505                                     depth, result_pool, scratch_pool),
10506     wcroot);
10507
10508   return SVN_NO_ERROR;
10509 }
10510
10511 svn_error_t *
10512 svn_wc__db_read_children_of_working_node(const apr_array_header_t **children,
10513                                          svn_wc__db_t *db,
10514                                          const char *local_abspath,
10515                                          apr_pool_t *result_pool,
10516                                          apr_pool_t *scratch_pool)
10517 {
10518   svn_wc__db_wcroot_t *wcroot;
10519   const char *local_relpath;
10520
10521   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10522
10523   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10524                                              local_abspath,
10525                                              scratch_pool, scratch_pool));
10526   VERIFY_USABLE_WCROOT(wcroot);
10527
10528   return gather_children2(children, wcroot, local_relpath,
10529                           result_pool, scratch_pool);
10530 }
10531
10532 /* Helper for svn_wc__db_node_check_replace().
10533  */
10534 static svn_error_t *
10535 check_replace_txn(svn_boolean_t *is_replace_root_p,
10536                   svn_boolean_t *base_replace_p,
10537                   svn_boolean_t *is_replace_p,
10538                   svn_wc__db_wcroot_t *wcroot,
10539                   const char *local_relpath,
10540                   apr_pool_t *scratch_pool)
10541 {
10542   svn_sqlite__stmt_t *stmt;
10543   svn_boolean_t have_row;
10544   svn_boolean_t is_replace = FALSE;
10545   int replaced_op_depth;
10546   svn_wc__db_status_t replaced_status;
10547
10548   /* Our caller initialized the output values to FALSE */
10549
10550   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10551                                     STMT_SELECT_NODE_INFO));
10552
10553   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10554
10555   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10556
10557   if (!have_row)
10558     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10559                              svn_sqlite__reset(stmt),
10560                              _("The node '%s' was not found."),
10561                              path_for_error_message(wcroot, local_relpath,
10562                                                     scratch_pool));
10563
10564   {
10565     svn_wc__db_status_t status;
10566
10567     status = svn_sqlite__column_token(stmt, 3, presence_map);
10568
10569     if (status != svn_wc__db_status_normal)
10570       return svn_error_trace(svn_sqlite__reset(stmt));
10571   }
10572
10573   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10574
10575   if (!have_row)
10576     return svn_error_trace(svn_sqlite__reset(stmt));
10577
10578   replaced_status = svn_sqlite__column_token(stmt, 3, presence_map);
10579
10580   /* If the layer below the add describes a not present or a deleted node,
10581      this is not a replacement. Deleted can only occur if an ancestor is
10582      the delete root. */
10583   if (replaced_status != svn_wc__db_status_not_present
10584       && replaced_status != svn_wc__db_status_excluded
10585       && replaced_status != svn_wc__db_status_server_excluded
10586       && replaced_status != svn_wc__db_status_base_deleted)
10587     {
10588       is_replace = TRUE;
10589       if (is_replace_p)
10590         *is_replace_p = TRUE;
10591     }
10592
10593   replaced_op_depth = svn_sqlite__column_int(stmt, 0);
10594
10595   if (base_replace_p)
10596     {
10597       int op_depth = svn_sqlite__column_int(stmt, 0);
10598
10599       while (op_depth != 0 && have_row)
10600         {
10601           SVN_ERR(svn_sqlite__step(&have_row, stmt));
10602
10603           if (have_row)
10604             op_depth = svn_sqlite__column_int(stmt, 0);
10605         }
10606
10607       if (have_row && op_depth == 0)
10608         {
10609           svn_wc__db_status_t base_status;
10610
10611           base_status = svn_sqlite__column_token(stmt, 3, presence_map);
10612
10613           *base_replace_p = (base_status != svn_wc__db_status_not_present);
10614         }
10615     }
10616
10617   SVN_ERR(svn_sqlite__reset(stmt));
10618
10619   if (!is_replace_root_p || !is_replace)
10620     return SVN_NO_ERROR;
10621
10622   if (replaced_status != svn_wc__db_status_base_deleted)
10623     {
10624       int parent_op_depth;
10625
10626       /* Check the current op-depth of the parent to see if we are a replacement
10627          root */
10628       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
10629                                 svn_relpath_dirname(local_relpath,
10630                                                     scratch_pool)));
10631
10632       SVN_ERR(svn_sqlite__step_row(stmt)); /* Parent must exist as 'normal' */
10633
10634       parent_op_depth = svn_sqlite__column_int(stmt, 0);
10635
10636       if (parent_op_depth >= replaced_op_depth)
10637         {
10638           /* Did we replace inside our directory? */
10639
10640           *is_replace_root_p = (parent_op_depth == replaced_op_depth);
10641           SVN_ERR(svn_sqlite__reset(stmt));
10642           return SVN_NO_ERROR;
10643         }
10644
10645       SVN_ERR(svn_sqlite__step(&have_row, stmt));
10646
10647       if (have_row)
10648         parent_op_depth = svn_sqlite__column_int(stmt, 0);
10649
10650       SVN_ERR(svn_sqlite__reset(stmt));
10651
10652       if (!have_row)
10653         *is_replace_root_p = TRUE; /* Parent is no replacement */
10654       else if (parent_op_depth < replaced_op_depth)
10655         *is_replace_root_p = TRUE; /* Parent replaces a lower layer */
10656       /*else // No replacement root */
10657   }
10658
10659   return SVN_NO_ERROR;
10660 }
10661
10662 svn_error_t *
10663 svn_wc__db_node_check_replace(svn_boolean_t *is_replace_root,
10664                               svn_boolean_t *base_replace,
10665                               svn_boolean_t *is_replace,
10666                               svn_wc__db_t *db,
10667                               const char *local_abspath,
10668                               apr_pool_t *scratch_pool)
10669 {
10670   svn_wc__db_wcroot_t *wcroot;
10671   const char *local_relpath;
10672
10673   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10674
10675   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10676                                              local_abspath,
10677                                              scratch_pool, scratch_pool));
10678   VERIFY_USABLE_WCROOT(wcroot);
10679
10680   if (is_replace_root)
10681     *is_replace_root = FALSE;
10682   if (base_replace)
10683     *base_replace = FALSE;
10684   if (is_replace)
10685     *is_replace = FALSE;
10686
10687   if (local_relpath[0] == '\0')
10688     return SVN_NO_ERROR; /* Working copy root can't be replaced */
10689
10690   SVN_WC__DB_WITH_TXN(
10691     check_replace_txn(is_replace_root, base_replace, is_replace,
10692                       wcroot, local_relpath, scratch_pool),
10693     wcroot);
10694
10695   return SVN_NO_ERROR;
10696 }
10697
10698 svn_error_t *
10699 svn_wc__db_read_children(const apr_array_header_t **children,
10700                          svn_wc__db_t *db,
10701                          const char *local_abspath,
10702                          apr_pool_t *result_pool,
10703                          apr_pool_t *scratch_pool)
10704 {
10705   svn_wc__db_wcroot_t *wcroot;
10706   const char *local_relpath;
10707
10708   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10709
10710   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10711                                              local_abspath,
10712                                              scratch_pool, scratch_pool));
10713   VERIFY_USABLE_WCROOT(wcroot);
10714
10715   return gather_children(children, wcroot, local_relpath,
10716                          result_pool, scratch_pool);
10717 }
10718
10719
10720 /* */
10721 static svn_error_t *
10722 relocate_txn(svn_wc__db_wcroot_t *wcroot,
10723              const char *local_relpath,
10724              const char *repos_root_url,
10725              const char *repos_uuid,
10726              svn_boolean_t have_base_node,
10727              apr_int64_t old_repos_id,
10728              apr_pool_t *scratch_pool)
10729 {
10730   svn_sqlite__stmt_t *stmt;
10731   apr_int64_t new_repos_id;
10732
10733   /* This function affects all the children of the given local_relpath,
10734      but the way that it does this is through the repos inheritance mechanism.
10735      So, we only need to rewrite the repos_id of the given local_relpath,
10736      as well as any children with a non-null repos_id, as well as various
10737      repos_id fields in the locks and working_node tables.
10738    */
10739
10740   /* Get the repos_id for the new repository. */
10741   SVN_ERR(create_repos_id(&new_repos_id, repos_root_url, repos_uuid,
10742                           wcroot->sdb, scratch_pool));
10743
10744   /* Set the (base and working) repos_ids and clear the dav_caches */
10745   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10746                                     STMT_RECURSIVE_UPDATE_NODE_REPO));
10747   SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
10748                             old_repos_id, new_repos_id));
10749   SVN_ERR(svn_sqlite__step_done(stmt));
10750
10751   if (have_base_node)
10752     {
10753       /* Update any locks for the root or its children. */
10754       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10755                                         STMT_UPDATE_LOCK_REPOS_ID));
10756       SVN_ERR(svn_sqlite__bindf(stmt, "ii", old_repos_id, new_repos_id));
10757       SVN_ERR(svn_sqlite__step_done(stmt));
10758     }
10759
10760   return SVN_NO_ERROR;
10761 }
10762
10763
10764 svn_error_t *
10765 svn_wc__db_global_relocate(svn_wc__db_t *db,
10766                            const char *local_dir_abspath,
10767                            const char *repos_root_url,
10768                            apr_pool_t *scratch_pool)
10769 {
10770   svn_wc__db_wcroot_t *wcroot;
10771   const char *local_relpath;
10772   const char *local_dir_relpath;
10773   svn_wc__db_status_t status;
10774   const char *repos_uuid;
10775   svn_boolean_t have_base_node;
10776   apr_int64_t old_repos_id;
10777
10778   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
10779   /* ### assert that we were passed a directory?  */
10780
10781   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_dir_relpath,
10782                            db, local_dir_abspath, scratch_pool, scratch_pool));
10783   VERIFY_USABLE_WCROOT(wcroot);
10784   local_relpath = local_dir_relpath;
10785
10786   SVN_ERR(read_info(&status,
10787                     NULL, NULL, NULL, &old_repos_id,
10788                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10789                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10790                     NULL,
10791                     &have_base_node, NULL, NULL,
10792                     wcroot, local_relpath,
10793                     scratch_pool, scratch_pool));
10794
10795   if (status == svn_wc__db_status_excluded)
10796     {
10797       /* The parent cannot be excluded, so look at the parent and then
10798          adjust the relpath */
10799       const char *parent_relpath = svn_relpath_dirname(local_dir_relpath,
10800                                                        scratch_pool);
10801       SVN_ERR(read_info(&status,
10802                         NULL, NULL, NULL, &old_repos_id,
10803                         NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10804                         NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10805                         NULL, NULL, NULL,
10806                         NULL, NULL, NULL,
10807                         wcroot, parent_relpath,
10808                         scratch_pool, scratch_pool));
10809       local_dir_relpath = parent_relpath;
10810     }
10811
10812   if (old_repos_id == INVALID_REPOS_ID)
10813     {
10814       /* Do we need to support relocating something that is
10815          added/deleted/excluded without relocating the parent?  If not
10816          then perhaps relpath, root_url and uuid should be passed down
10817          to the children so that they don't have to scan? */
10818
10819       if (status == svn_wc__db_status_deleted)
10820         {
10821           const char *work_del_relpath;
10822
10823           SVN_ERR(scan_deletion_txn(NULL, NULL,
10824                                     &work_del_relpath, NULL,
10825                                     wcroot, local_dir_relpath,
10826                                     scratch_pool,
10827                                     scratch_pool));
10828           if (work_del_relpath)
10829             {
10830               /* Deleted within a copy/move */
10831
10832               /* The parent of the delete is added. */
10833               status = svn_wc__db_status_added;
10834               local_dir_relpath = svn_relpath_dirname(work_del_relpath,
10835                                                       scratch_pool);
10836             }
10837         }
10838
10839       if (status == svn_wc__db_status_added)
10840         {
10841           SVN_ERR(scan_addition(NULL, NULL, NULL, &old_repos_id,
10842                                 NULL, NULL, NULL, NULL, NULL, NULL,
10843                                 wcroot, local_dir_relpath,
10844                                 scratch_pool, scratch_pool));
10845         }
10846       else
10847         SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL,
10848                                                   &old_repos_id,
10849                                                   NULL, NULL, NULL, NULL, NULL,
10850                                                   NULL, NULL, NULL, NULL, NULL,
10851                                                   wcroot, local_dir_relpath,
10852                                                   scratch_pool, scratch_pool));
10853     }
10854
10855   SVN_ERR(svn_wc__db_fetch_repos_info(NULL, &repos_uuid, wcroot->sdb,
10856                                       old_repos_id, scratch_pool));
10857   SVN_ERR_ASSERT(repos_uuid);
10858
10859   SVN_WC__DB_WITH_TXN(
10860     relocate_txn(wcroot, local_relpath, repos_root_url, repos_uuid,
10861                  have_base_node, old_repos_id, scratch_pool),
10862     wcroot);
10863
10864   return SVN_NO_ERROR;
10865 }
10866
10867
10868 /* Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of
10869    (WCROOT, LOCAL_RELPATH), directly if its BASE row exists or implied from
10870    its parent's BASE row if not. In the latter case, error if the parent
10871    BASE row does not exist.  */
10872 static svn_error_t *
10873 determine_repos_info(apr_int64_t *repos_id,
10874                      const char **repos_relpath,
10875                      svn_wc__db_wcroot_t *wcroot,
10876                      const char *local_relpath,
10877                      apr_pool_t *result_pool,
10878                      apr_pool_t *scratch_pool)
10879 {
10880   svn_sqlite__stmt_t *stmt;
10881   svn_boolean_t have_row;
10882   const char *repos_parent_relpath;
10883   const char *local_parent_relpath, *name;
10884
10885   /* ### is it faster to fetch fewer columns? */
10886
10887   /* Prefer the current node's repository information.  */
10888   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10889                                     STMT_SELECT_BASE_NODE));
10890   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10891   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10892
10893   if (have_row)
10894     {
10895       SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 0));
10896       SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1));
10897
10898       *repos_id = svn_sqlite__column_int64(stmt, 0);
10899       *repos_relpath = svn_sqlite__column_text(stmt, 1, result_pool);
10900
10901       return svn_error_trace(svn_sqlite__reset(stmt));
10902     }
10903
10904   SVN_ERR(svn_sqlite__reset(stmt));
10905
10906   /* This was a child node within this wcroot. We want to look at the
10907      BASE node of the directory.  */
10908   svn_relpath_split(&local_parent_relpath, &name, local_relpath, scratch_pool);
10909
10910   /* The REPOS_ID will be the same (### until we support mixed-repos)  */
10911   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
10912                                             &repos_parent_relpath, repos_id,
10913                                             NULL, NULL, NULL, NULL, NULL,
10914                                             NULL, NULL, NULL, NULL, NULL,
10915                                             wcroot, local_parent_relpath,
10916                                             scratch_pool, scratch_pool));
10917
10918   *repos_relpath = svn_relpath_join(repos_parent_relpath, name, result_pool);
10919
10920   return SVN_NO_ERROR;
10921 }
10922
10923 /* Helper for svn_wc__db_global_commit()
10924
10925    Makes local_relpath and all its descendants at the same op-depth represent
10926    the copy origin repos_id:repos_relpath@revision.
10927
10928    This code is only valid to fix-up a move from an old location, to a new
10929    location during a commit.
10930
10931    Assumptions:
10932      * local_relpath is not the working copy root (can't be moved)
10933      * repos_relpath is not the repository root (can't be moved)
10934    */
10935 static svn_error_t *
10936 moved_descendant_commit(svn_wc__db_wcroot_t *wcroot,
10937                         const char *local_relpath,
10938                         int op_depth,
10939                         apr_int64_t repos_id,
10940                         const char *repos_relpath,
10941                         svn_revnum_t revision,
10942                         apr_pool_t *scratch_pool)
10943 {
10944   apr_hash_t *children;
10945   apr_pool_t *iterpool;
10946   svn_sqlite__stmt_t *stmt;
10947   svn_boolean_t have_row;
10948   apr_hash_index_t *hi;
10949
10950   SVN_ERR_ASSERT(*local_relpath != '\0'
10951                  && *repos_relpath != '\0');
10952
10953   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10954                                     STMT_SELECT_MOVED_DESCENDANTS));
10955   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
10956                                          local_relpath,
10957                                          op_depth));
10958
10959   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10960   if (! have_row)
10961     return svn_error_trace(svn_sqlite__reset(stmt));
10962
10963   children = apr_hash_make(scratch_pool);
10964
10965   /* First, obtain all moved children */
10966   /* To keep error handling simple, first cache them in a hashtable */
10967   while (have_row)
10968     {
10969       const char *src_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
10970       const char *to_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
10971
10972       svn_hash_sets(children, src_relpath, to_relpath);
10973
10974       SVN_ERR(svn_sqlite__step(&have_row, stmt));
10975     }
10976   SVN_ERR(svn_sqlite__reset(stmt));
10977
10978   /* Then update them */
10979   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10980                                     STMT_COMMIT_UPDATE_ORIGIN));
10981
10982   iterpool = svn_pool_create(scratch_pool);
10983   for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
10984     {
10985       const char *src_relpath = svn__apr_hash_index_key(hi);
10986       const char *to_relpath = svn__apr_hash_index_val(hi);
10987       const char *new_repos_relpath;
10988       int to_op_depth = relpath_depth(to_relpath);
10989       int affected;
10990
10991       svn_pool_clear(iterpool);
10992
10993       SVN_ERR_ASSERT(to_op_depth > 0);
10994
10995       new_repos_relpath = svn_relpath_join(
10996                             repos_relpath,
10997                             svn_relpath_skip_ancestor(local_relpath,
10998                                                       src_relpath),
10999                             iterpool);
11000
11001       SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11002                                                 to_relpath,
11003                                                 to_op_depth,
11004                                                 repos_id,
11005                                                 new_repos_relpath,
11006                                                 revision));
11007       SVN_ERR(svn_sqlite__update(&affected, stmt));
11008
11009 #ifdef SVN_DEBUG
11010       /* Enable in release code?
11011          Broken moves are not fatal yet, but this assertion would break
11012          committing them */
11013       SVN_ERR_ASSERT(affected >= 1); /* If this fails there is no move dest */
11014 #endif
11015
11016       SVN_ERR(moved_descendant_commit(wcroot, to_relpath, to_op_depth,
11017                                       repos_id, new_repos_relpath, revision,
11018                                       iterpool));
11019     }
11020
11021   svn_pool_destroy(iterpool);
11022   return SVN_NO_ERROR;
11023 }
11024
11025 /* Helper for svn_wc__db_global_commit()
11026
11027    Moves all nodes below LOCAL_RELPATH from op-depth OP_DEPTH to op-depth 0
11028    (BASE), setting their presence to 'not-present' if their presence wasn't
11029    'normal'.
11030
11031    Makes all nodes below LOCAL_RELPATH represent the descendants of repository
11032    location repos_id:repos_relpath@revision.
11033
11034    Assumptions:
11035      * local_relpath is not the working copy root (can't be replaced)
11036      * repos_relpath is not the repository root (can't be replaced)
11037    */
11038 static svn_error_t *
11039 descendant_commit(svn_wc__db_wcroot_t *wcroot,
11040                   const char *local_relpath,
11041                   int op_depth,
11042                   apr_int64_t repos_id,
11043                   const char *repos_relpath,
11044                   svn_revnum_t revision,
11045                   apr_pool_t *scratch_pool)
11046 {
11047   svn_sqlite__stmt_t *stmt;
11048
11049   SVN_ERR_ASSERT(*local_relpath != '\0'
11050                  && *repos_relpath != '\0');
11051
11052   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11053                                     STMT_COMMIT_DESCENDANTS_TO_BASE));
11054
11055   SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11056                                             local_relpath,
11057                                             op_depth,
11058                                             repos_id,
11059                                             repos_relpath,
11060                                             revision));
11061
11062   SVN_ERR(svn_sqlite__update(NULL, stmt));
11063
11064   return SVN_NO_ERROR;
11065 }
11066
11067 /* The body of svn_wc__db_global_commit().
11068  */
11069 static svn_error_t *
11070 commit_node(svn_wc__db_wcroot_t *wcroot,
11071             const char *local_relpath,
11072             svn_revnum_t new_revision,
11073             svn_revnum_t changed_rev,
11074             apr_time_t changed_date,
11075             const char *changed_author,
11076             const svn_checksum_t *new_checksum,
11077             const apr_array_header_t *new_children,
11078             apr_hash_t *new_dav_cache,
11079             svn_boolean_t keep_changelist,
11080             svn_boolean_t no_unlock,
11081             const svn_skel_t *work_items,
11082             apr_pool_t *scratch_pool)
11083 {
11084   svn_sqlite__stmt_t *stmt_info;
11085   svn_sqlite__stmt_t *stmt_act;
11086   svn_boolean_t have_act;
11087   svn_string_t prop_blob = { 0 };
11088   svn_string_t inherited_prop_blob = { 0 };
11089   const char *changelist = NULL;
11090   const char *parent_relpath;
11091   svn_wc__db_status_t new_presence;
11092   svn_node_kind_t new_kind;
11093   const char *new_depth_str = NULL;
11094   svn_sqlite__stmt_t *stmt;
11095   apr_int64_t repos_id;
11096   const char *repos_relpath;
11097   int op_depth;
11098   svn_wc__db_status_t old_presence;
11099
11100     /* If we are adding a file or directory, then we need to get
11101      repository information from the parent node since "this node" does
11102      not have a BASE).
11103
11104      For existing nodes, we should retain the (potentially-switched)
11105      repository information.  */
11106   SVN_ERR(determine_repos_info(&repos_id, &repos_relpath,
11107                                wcroot, local_relpath,
11108                                scratch_pool, scratch_pool));
11109
11110   /* ### is it better to select only the data needed?  */
11111   SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
11112                                     STMT_SELECT_NODE_INFO));
11113   SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
11114   SVN_ERR(svn_sqlite__step_row(stmt_info));
11115
11116   SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
11117                                     STMT_SELECT_ACTUAL_NODE));
11118   SVN_ERR(svn_sqlite__bindf(stmt_act, "is",
11119                             wcroot->wc_id, local_relpath));
11120   SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
11121
11122   /* There should be something to commit!  */
11123
11124   op_depth = svn_sqlite__column_int(stmt_info, 0);
11125
11126   /* Figure out the new node's kind. It will be whatever is in WORKING_NODE,
11127      or there will be a BASE_NODE that has it.  */
11128   new_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
11129
11130   /* What will the new depth be?  */
11131   if (new_kind == svn_node_dir)
11132     new_depth_str = svn_sqlite__column_text(stmt_info, 11, scratch_pool);
11133
11134   /* Check that the repository information is not being changed.  */
11135   if (op_depth == 0)
11136     {
11137       SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 1));
11138       SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 2));
11139
11140       /* A commit cannot change these values.  */
11141       SVN_ERR_ASSERT(repos_id == svn_sqlite__column_int64(stmt_info, 1));
11142       SVN_ERR_ASSERT(strcmp(repos_relpath,
11143                             svn_sqlite__column_text(stmt_info, 2, NULL)) == 0);
11144     }
11145
11146   /* Find the appropriate new properties -- ACTUAL overrides any properties
11147      in WORKING that arrived as part of a copy/move.
11148
11149      Note: we'll keep them as a big blob of data, rather than
11150      deserialize/serialize them.  */
11151   if (have_act)
11152     prop_blob.data = svn_sqlite__column_blob(stmt_act, 1, &prop_blob.len,
11153                                              scratch_pool);
11154   if (prop_blob.data == NULL)
11155     prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len,
11156                                              scratch_pool);
11157
11158   inherited_prop_blob.data = svn_sqlite__column_blob(stmt_info, 16,
11159                                                      &inherited_prop_blob.len,
11160                                                      scratch_pool);
11161
11162   if (keep_changelist && have_act)
11163     changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool);
11164
11165   old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map);
11166
11167   /* ### other stuff?  */
11168
11169   SVN_ERR(svn_sqlite__reset(stmt_info));
11170   SVN_ERR(svn_sqlite__reset(stmt_act));
11171
11172   if (op_depth > 0)
11173     {
11174       int affected_rows;
11175
11176       /* This removes all layers of this node and at the same time determines
11177          if we need to remove shadowed layers below our descendants. */
11178
11179       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11180                                         STMT_DELETE_NODE_ALL_LAYERS));
11181       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11182       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
11183
11184       if (affected_rows > 1)
11185         {
11186           /* We commit a shadowing operation
11187
11188            1) Remove all shadowed nodes
11189            2) And remove all nodes that have a base-deleted as lowest layer,
11190               because 1) removed that layer */
11191
11192           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11193                                             STMT_DELETE_SHADOWED_RECURSIVE));
11194
11195           SVN_ERR(svn_sqlite__bindf(stmt,
11196                                     "isd",
11197                                     wcroot->wc_id,
11198                                     local_relpath,
11199                                     op_depth));
11200
11201           SVN_ERR(svn_sqlite__step_done(stmt));
11202         }
11203
11204       /* Note that while these two calls look so similar that they might
11205          be integrated, they really affect a different op-depth and
11206          completely different nodes (via a different recursion pattern). */
11207
11208       /* Collapse descendants of the current op_depth in layer 0 */
11209       SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth,
11210                                 repos_id, repos_relpath, new_revision,
11211                                 scratch_pool));
11212
11213       /* And make the recorded local moves represent moves of the node we just
11214          committed. */
11215       SVN_ERR(moved_descendant_commit(wcroot, local_relpath, 0,
11216                                       repos_id, repos_relpath, new_revision,
11217                                       scratch_pool));
11218
11219       /* This node is no longer modified, so no node was moved here */
11220       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11221                                         STMT_CLEAR_MOVED_TO_FROM_DEST));
11222       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
11223                                             local_relpath));
11224
11225       SVN_ERR(svn_sqlite__step_done(stmt));
11226     }
11227
11228   /* Update or add the BASE_NODE row with all the new information.  */
11229
11230   if (*local_relpath == '\0')
11231     parent_relpath = NULL;
11232   else
11233     parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
11234
11235   /* Preserve any incomplete status */
11236   new_presence = (old_presence == svn_wc__db_status_incomplete
11237                   ? svn_wc__db_status_incomplete
11238                   : svn_wc__db_status_normal);
11239
11240   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11241                                     STMT_APPLY_CHANGES_TO_BASE_NODE));
11242   /* symlink_target not yet used */
11243   SVN_ERR(svn_sqlite__bindf(stmt, "issisrtstrisnbn",
11244                             wcroot->wc_id, local_relpath,
11245                             parent_relpath,
11246                             repos_id,
11247                             repos_relpath,
11248                             new_revision,
11249                             presence_map, new_presence,
11250                             new_depth_str,
11251                             kind_map, new_kind,
11252                             changed_rev,
11253                             changed_date,
11254                             changed_author,
11255                             prop_blob.data, prop_blob.len));
11256
11257   SVN_ERR(svn_sqlite__bind_checksum(stmt, 13, new_checksum,
11258                                     scratch_pool));
11259   SVN_ERR(svn_sqlite__bind_properties(stmt, 15, new_dav_cache,
11260                                       scratch_pool));
11261   if (inherited_prop_blob.data != NULL)
11262     {
11263       SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data,
11264                                     inherited_prop_blob.len));
11265     }
11266
11267   SVN_ERR(svn_sqlite__step_done(stmt));
11268
11269   if (have_act)
11270     {
11271       if (keep_changelist && changelist != NULL)
11272         {
11273           /* The user told us to keep the changelist. Replace the row in
11274              ACTUAL_NODE with the basic keys and the changelist.  */
11275           SVN_ERR(svn_sqlite__get_statement(
11276                     &stmt, wcroot->sdb,
11277                     STMT_RESET_ACTUAL_WITH_CHANGELIST));
11278           SVN_ERR(svn_sqlite__bindf(stmt, "isss",
11279                                     wcroot->wc_id, local_relpath,
11280                                     svn_relpath_dirname(local_relpath,
11281                                                         scratch_pool),
11282                                     changelist));
11283           SVN_ERR(svn_sqlite__step_done(stmt));
11284         }
11285       else
11286         {
11287           /* Toss the ACTUAL_NODE row.  */
11288           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11289                                             STMT_DELETE_ACTUAL_NODE));
11290           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11291           SVN_ERR(svn_sqlite__step_done(stmt));
11292         }
11293     }
11294
11295   if (new_kind == svn_node_dir)
11296     {
11297       /* When committing a directory, we should have its new children.  */
11298       /* ### one day. just not today.  */
11299 #if 0
11300       SVN_ERR_ASSERT(new_children != NULL);
11301 #endif
11302
11303       /* ### process the children  */
11304     }
11305
11306   if (!no_unlock)
11307     {
11308       svn_sqlite__stmt_t *lock_stmt;
11309
11310       SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
11311                                         STMT_DELETE_LOCK_RECURSIVELY));
11312       SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
11313       SVN_ERR(svn_sqlite__step_done(lock_stmt));
11314     }
11315
11316   /* Install any work items into the queue, as part of this transaction.  */
11317   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
11318
11319   return SVN_NO_ERROR;
11320 }
11321
11322
11323 svn_error_t *
11324 svn_wc__db_global_commit(svn_wc__db_t *db,
11325                          const char *local_abspath,
11326                          svn_revnum_t new_revision,
11327                          svn_revnum_t changed_revision,
11328                          apr_time_t changed_date,
11329                          const char *changed_author,
11330                          const svn_checksum_t *new_checksum,
11331                          const apr_array_header_t *new_children,
11332                          apr_hash_t *new_dav_cache,
11333                          svn_boolean_t keep_changelist,
11334                          svn_boolean_t no_unlock,
11335                          const svn_skel_t *work_items,
11336                          apr_pool_t *scratch_pool)
11337 {
11338   const char *local_relpath;
11339   svn_wc__db_wcroot_t *wcroot;
11340
11341   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11342   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
11343   SVN_ERR_ASSERT(new_checksum == NULL || new_children == NULL);
11344
11345   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11346                               local_abspath, scratch_pool, scratch_pool));
11347   VERIFY_USABLE_WCROOT(wcroot);
11348
11349   SVN_WC__DB_WITH_TXN(
11350     commit_node(wcroot, local_relpath,
11351                 new_revision, changed_revision, changed_date, changed_author,
11352                 new_checksum, new_children, new_dav_cache, keep_changelist,
11353                 no_unlock, work_items, scratch_pool),
11354     wcroot);
11355
11356   /* We *totally* monkeyed the entries. Toss 'em.  */
11357   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
11358
11359   return SVN_NO_ERROR;
11360 }
11361
11362
11363 svn_error_t *
11364 svn_wc__db_global_update(svn_wc__db_t *db,
11365                          const char *local_abspath,
11366                          svn_node_kind_t new_kind,
11367                          const char *new_repos_relpath,
11368                          svn_revnum_t new_revision,
11369                          const apr_hash_t *new_props,
11370                          svn_revnum_t new_changed_rev,
11371                          apr_time_t new_changed_date,
11372                          const char *new_changed_author,
11373                          const apr_array_header_t *new_children,
11374                          const svn_checksum_t *new_checksum,
11375                          const char *new_target,
11376                          const apr_hash_t *new_dav_cache,
11377                          const svn_skel_t *conflict,
11378                          const svn_skel_t *work_items,
11379                          apr_pool_t *scratch_pool)
11380 {
11381   NOT_IMPLEMENTED();
11382
11383 #if 0
11384   svn_wc__db_wcroot_t *wcroot;
11385   const char *local_relpath;
11386
11387   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11388   /* ### allow NULL for NEW_REPOS_RELPATH to indicate "no change"?  */
11389   SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
11390   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
11391   SVN_ERR_ASSERT(new_props != NULL);
11392   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_changed_rev));
11393   SVN_ERR_ASSERT((new_children != NULL
11394                   && new_checksum == NULL
11395                   && new_target == NULL)
11396                  || (new_children == NULL
11397                      && new_checksum != NULL
11398                      && new_target == NULL)
11399                  || (new_children == NULL
11400                      && new_checksum == NULL
11401                      && new_target != NULL));
11402
11403   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11404                               local_abspath, scratch_pool, scratch_pool));
11405   VERIFY_USABLE_WCROOT(wcroot);
11406
11407   SVN_WC__DB_WITH_TXN(
11408     update_node(wcroot, local_relpath,
11409                 new_repos_relpath, new_revision, new_props,
11410                 new_changed_rev, new_changed_date, new_changed_author,
11411                 new_children, new_checksum, new_target,
11412                 conflict, work_items, scratch_pool),
11413     wcroot);
11414
11415   /* We *totally* monkeyed the entries. Toss 'em.  */
11416   SVN_ERR(flush_entries(wcroot, local_abspath, scratch_pool));
11417
11418   return SVN_NO_ERROR;
11419 #endif
11420 }
11421
11422 /* Sets a base nodes revision, repository relative path, and/or inherited
11423    propertis. If LOCAL_ABSPATH's rev (REV) is valid, set its revision.  If
11424    SET_REPOS_RELPATH is TRUE set its repository relative path to REPOS_RELPATH
11425    (and make sure its REPOS_ID is still valid).  If IPROPS is not NULL set its
11426    inherited properties to IPROPS, if IPROPS is NULL then clear any the iprops
11427    cache for the base node.
11428  */
11429 static svn_error_t *
11430 db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot,
11431                                    const char *local_relpath,
11432                                    apr_array_header_t *iprops,
11433                                    svn_revnum_t rev,
11434                                    svn_boolean_t set_repos_relpath,
11435                                    const char *repos_relpath,
11436                                    apr_int64_t repos_id,
11437                                    apr_pool_t *scratch_pool)
11438 {
11439   svn_sqlite__stmt_t *stmt;
11440
11441   SVN_ERR(flush_entries(wcroot,
11442                         svn_dirent_join(wcroot->abspath, local_relpath,
11443                                         scratch_pool),
11444                         svn_depth_empty, scratch_pool));
11445
11446
11447   if (SVN_IS_VALID_REVNUM(rev))
11448     {
11449       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11450                                         STMT_UPDATE_BASE_REVISION));
11451
11452       SVN_ERR(svn_sqlite__bindf(stmt, "isr", wcroot->wc_id, local_relpath,
11453                                 rev));
11454
11455       SVN_ERR(svn_sqlite__step_done(stmt));
11456     }
11457
11458   if (set_repos_relpath)
11459     {
11460       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11461                                         STMT_UPDATE_BASE_REPOS));
11462
11463       SVN_ERR(svn_sqlite__bindf(stmt, "isis", wcroot->wc_id, local_relpath,
11464                                 repos_id, repos_relpath));
11465
11466       SVN_ERR(svn_sqlite__step_done(stmt));
11467     }
11468
11469   /* Set or clear iprops. */
11470   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11471                                     STMT_UPDATE_IPROP));
11472   SVN_ERR(svn_sqlite__bindf(stmt, "is",
11473                             wcroot->wc_id,
11474                             local_relpath));
11475   SVN_ERR(svn_sqlite__bind_iprops(stmt, 3, iprops, scratch_pool));
11476   SVN_ERR(svn_sqlite__step_done(stmt));
11477
11478   return SVN_NO_ERROR;
11479 }
11480
11481 /* The main body of bump_revisions_post_update().
11482  *
11483  * Tweak the information for LOCAL_RELPATH in WCROOT.  If NEW_REPOS_RELPATH is
11484  * non-NULL update the entry to the new url specified by NEW_REPOS_RELPATH,
11485  * NEW_REPOS_ID.  If NEW_REV is valid, make this the node's working revision.
11486  *
11487  * If WCROOT_IPROPS is not NULL it is a hash mapping const char * absolute
11488  * working copy paths to depth-first ordered arrays of
11489  * svn_prop_inherited_item_t * structures.  If the absolute path equivalent
11490  * of LOCAL_RELPATH exists in WCROOT_IPROPS, then set the hashed value as the
11491  * node's inherited properties.
11492  *
11493  * Unless S_ROOT is TRUE the tweaks might cause the node for LOCAL_ABSPATH to
11494  * be removed from the WC; if IS_ROOT is TRUE this will not happen.
11495  */
11496 static svn_error_t *
11497 bump_node_revision(svn_wc__db_wcroot_t *wcroot,
11498                    const char *local_relpath,
11499                    apr_int64_t new_repos_id,
11500                    const char *new_repos_relpath,
11501                    svn_revnum_t new_rev,
11502                    svn_depth_t depth,
11503                    apr_hash_t *exclude_relpaths,
11504                    apr_hash_t *wcroot_iprops,
11505                    svn_boolean_t is_root,
11506                    svn_boolean_t skip_when_dir,
11507                    svn_wc__db_t *db,
11508                    apr_pool_t *scratch_pool)
11509 {
11510   apr_pool_t *iterpool;
11511   const apr_array_header_t *children;
11512   int i;
11513   svn_wc__db_status_t status;
11514   svn_node_kind_t db_kind;
11515   svn_revnum_t revision;
11516   const char *repos_relpath;
11517   apr_int64_t repos_id;
11518   svn_boolean_t set_repos_relpath = FALSE;
11519   svn_boolean_t update_root;
11520   svn_depth_t depth_below_here = depth;
11521   apr_array_header_t *iprops = NULL;
11522
11523   /* Skip an excluded path and its descendants. */
11524   if (svn_hash_gets(exclude_relpaths, local_relpath))
11525     return SVN_NO_ERROR;
11526
11527   SVN_ERR(svn_wc__db_base_get_info_internal(&status, &db_kind, &revision,
11528                                             &repos_relpath, &repos_id,
11529                                             NULL, NULL, NULL, NULL, NULL,
11530                                             NULL, NULL, NULL, NULL, &update_root,
11531                                             wcroot, local_relpath,
11532                                             scratch_pool, scratch_pool));
11533
11534   /* Skip file externals */
11535   if (update_root
11536       && db_kind == svn_node_file
11537       && !is_root)
11538     return SVN_NO_ERROR;
11539
11540   if (skip_when_dir && db_kind == svn_node_dir)
11541     return SVN_NO_ERROR;
11542
11543   /* If the node is still marked 'not-present', then the server did not
11544      re-add it.  So it's really gone in this revision, thus we remove the node.
11545
11546      If the node is still marked 'server-excluded' and yet is not the same
11547      revision as new_rev, then the server did not re-add it, nor
11548      re-server-exclude it, so we can remove the node. */
11549   if (!is_root
11550       && (status == svn_wc__db_status_not_present
11551           || (status == svn_wc__db_status_server_excluded &&
11552               revision != new_rev)))
11553     {
11554       return svn_error_trace(db_base_remove(wcroot, local_relpath,
11555                                             db, FALSE, FALSE, FALSE,
11556                                             SVN_INVALID_REVNUM,
11557                                             NULL, NULL, scratch_pool));
11558     }
11559
11560   if (new_repos_relpath != NULL && strcmp(repos_relpath, new_repos_relpath))
11561     set_repos_relpath = TRUE;
11562
11563   if (wcroot_iprops)
11564     iprops = svn_hash_gets(wcroot_iprops,
11565                            svn_dirent_join(wcroot->abspath, local_relpath,
11566                                            scratch_pool));
11567
11568   if (iprops
11569       || set_repos_relpath
11570       || (SVN_IS_VALID_REVNUM(new_rev) && new_rev != revision))
11571     {
11572       SVN_ERR(db_op_set_rev_repos_relpath_iprops(wcroot, local_relpath,
11573                                                  iprops, new_rev,
11574                                                  set_repos_relpath,
11575                                                  new_repos_relpath,
11576                                                  new_repos_id,
11577                                                  scratch_pool));
11578     }
11579
11580   /* Early out */
11581   if (depth <= svn_depth_empty
11582       || db_kind != svn_node_dir
11583       || status == svn_wc__db_status_server_excluded
11584       || status == svn_wc__db_status_excluded
11585       || status == svn_wc__db_status_not_present)
11586     return SVN_NO_ERROR;
11587
11588   /* And now recurse over the children */
11589
11590   depth_below_here = depth;
11591
11592   if (depth == svn_depth_immediates || depth == svn_depth_files)
11593     depth_below_here = svn_depth_empty;
11594
11595   iterpool = svn_pool_create(scratch_pool);
11596
11597   SVN_ERR(gather_repo_children(&children, wcroot, local_relpath, 0,
11598                                scratch_pool, iterpool));
11599   for (i = 0; i < children->nelts; i++)
11600     {
11601       const char *child_basename = APR_ARRAY_IDX(children, i, const char *);
11602       const char *child_local_relpath;
11603       const char *child_repos_relpath = NULL;
11604
11605       svn_pool_clear(iterpool);
11606
11607       /* Derive the new URL for the current (child) entry */
11608       if (new_repos_relpath)
11609         child_repos_relpath = svn_relpath_join(new_repos_relpath,
11610                                                child_basename, iterpool);
11611
11612       child_local_relpath = svn_relpath_join(local_relpath, child_basename,
11613                                              iterpool);
11614
11615       SVN_ERR(bump_node_revision(wcroot, child_local_relpath, new_repos_id,
11616                                  child_repos_relpath, new_rev,
11617                                  depth_below_here,
11618                                  exclude_relpaths, wcroot_iprops,
11619                                  FALSE /* is_root */,
11620                                  (depth < svn_depth_immediates), db,
11621                                  iterpool));
11622     }
11623
11624   /* Cleanup */
11625   svn_pool_destroy(iterpool);
11626
11627   return SVN_NO_ERROR;
11628 }
11629
11630 /* Helper for svn_wc__db_op_bump_revisions_post_update().
11631  */
11632 static svn_error_t *
11633 bump_revisions_post_update(svn_wc__db_wcroot_t *wcroot,
11634                            const char *local_relpath,
11635                            svn_wc__db_t *db,
11636                            svn_depth_t depth,
11637                            const char *new_repos_relpath,
11638                            const char *new_repos_root_url,
11639                            const char *new_repos_uuid,
11640                            svn_revnum_t new_revision,
11641                            apr_hash_t *exclude_relpaths,
11642                            apr_hash_t *wcroot_iprops,
11643                            svn_wc_notify_func2_t notify_func,
11644                            void *notify_baton,
11645                            apr_pool_t *scratch_pool)
11646 {
11647   svn_wc__db_status_t status;
11648   svn_node_kind_t kind;
11649   svn_error_t *err;
11650   apr_int64_t new_repos_id = INVALID_REPOS_ID;
11651
11652   err = svn_wc__db_base_get_info_internal(&status, &kind, NULL, NULL, NULL,
11653                                           NULL, NULL, NULL, NULL, NULL, NULL,
11654                                           NULL, NULL, NULL, NULL,
11655                                           wcroot, local_relpath,
11656                                           scratch_pool, scratch_pool);
11657   if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
11658     {
11659       svn_error_clear(err);
11660       return SVN_NO_ERROR;
11661     }
11662   else
11663     SVN_ERR(err);
11664
11665   switch (status)
11666     {
11667       case svn_wc__db_status_excluded:
11668       case svn_wc__db_status_server_excluded:
11669       case svn_wc__db_status_not_present:
11670         return SVN_NO_ERROR;
11671
11672       /* Explicitly ignore other statii */
11673       default:
11674         break;
11675     }
11676
11677   if (new_repos_root_url != NULL)
11678     SVN_ERR(create_repos_id(&new_repos_id, new_repos_root_url,
11679                             new_repos_uuid,
11680                             wcroot->sdb, scratch_pool));
11681
11682   SVN_ERR(bump_node_revision(wcroot, local_relpath, new_repos_id,
11683                              new_repos_relpath, new_revision,
11684                              depth, exclude_relpaths,
11685                              wcroot_iprops,
11686                              TRUE /* is_root */, FALSE, db,
11687                              scratch_pool));
11688
11689   SVN_ERR(svn_wc__db_bump_moved_away(wcroot, local_relpath, depth, db,
11690                                      scratch_pool));
11691
11692   SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, SVN_INVALID_REVNUM,
11693                                              SVN_INVALID_REVNUM, notify_func,
11694                                              notify_baton, scratch_pool));
11695
11696   return SVN_NO_ERROR;
11697 }
11698
11699 svn_error_t *
11700 svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db,
11701                                          const char *local_abspath,
11702                                          svn_depth_t depth,
11703                                          const char *new_repos_relpath,
11704                                          const char *new_repos_root_url,
11705                                          const char *new_repos_uuid,
11706                                          svn_revnum_t new_revision,
11707                                          apr_hash_t *exclude_relpaths,
11708                                          apr_hash_t *wcroot_iprops,
11709                                          svn_wc_notify_func2_t notify_func,
11710                                          void *notify_baton,
11711                                          apr_pool_t *scratch_pool)
11712 {
11713   const char *local_relpath;
11714   svn_wc__db_wcroot_t *wcroot;
11715
11716   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11717                               local_abspath, scratch_pool, scratch_pool));
11718
11719   VERIFY_USABLE_WCROOT(wcroot);
11720
11721   if (svn_hash_gets(exclude_relpaths, local_relpath))
11722     return SVN_NO_ERROR;
11723
11724   if (depth == svn_depth_unknown)
11725     depth = svn_depth_infinity;
11726
11727   SVN_WC__DB_WITH_TXN(
11728     bump_revisions_post_update(wcroot, local_relpath, db,
11729                                depth, new_repos_relpath, new_repos_root_url,
11730                                new_repos_uuid, new_revision,
11731                                exclude_relpaths, wcroot_iprops,
11732                                notify_func, notify_baton, scratch_pool),
11733     wcroot);
11734
11735   return SVN_NO_ERROR;
11736 }
11737
11738 /* The body of svn_wc__db_lock_add().
11739  */
11740 static svn_error_t *
11741 lock_add_txn(svn_wc__db_wcroot_t *wcroot,
11742              const char *local_relpath,
11743              const svn_wc__db_lock_t *lock,
11744              apr_pool_t *scratch_pool)
11745 {
11746   svn_sqlite__stmt_t *stmt;
11747   const char *repos_relpath;
11748   apr_int64_t repos_id;
11749
11750   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11751                                             &repos_relpath, &repos_id,
11752                                             NULL, NULL, NULL, NULL, NULL,
11753                                             NULL, NULL, NULL, NULL, NULL,
11754                                             wcroot, local_relpath,
11755                                             scratch_pool, scratch_pool));
11756
11757   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_LOCK));
11758   SVN_ERR(svn_sqlite__bindf(stmt, "iss",
11759                             repos_id, repos_relpath, lock->token));
11760
11761   if (lock->owner != NULL)
11762     SVN_ERR(svn_sqlite__bind_text(stmt, 4, lock->owner));
11763
11764   if (lock->comment != NULL)
11765     SVN_ERR(svn_sqlite__bind_text(stmt, 5, lock->comment));
11766
11767   if (lock->date != 0)
11768     SVN_ERR(svn_sqlite__bind_int64(stmt, 6, lock->date));
11769
11770   SVN_ERR(svn_sqlite__insert(NULL, stmt));
11771
11772   return SVN_NO_ERROR;
11773 }
11774
11775
11776 svn_error_t *
11777 svn_wc__db_lock_add(svn_wc__db_t *db,
11778                     const char *local_abspath,
11779                     const svn_wc__db_lock_t *lock,
11780                     apr_pool_t *scratch_pool)
11781 {
11782   svn_wc__db_wcroot_t *wcroot;
11783   const char *local_relpath;
11784
11785   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11786   SVN_ERR_ASSERT(lock != NULL);
11787
11788   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11789                               local_abspath, scratch_pool, scratch_pool));
11790   VERIFY_USABLE_WCROOT(wcroot);
11791
11792   SVN_WC__DB_WITH_TXN(
11793     lock_add_txn(wcroot, local_relpath, lock, scratch_pool),
11794     wcroot);
11795
11796   /* There may be some entries, and the lock info is now out of date.  */
11797   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
11798
11799   return SVN_NO_ERROR;
11800 }
11801
11802
11803 /* The body of svn_wc__db_lock_remove().
11804  */
11805 static svn_error_t *
11806 lock_remove_txn(svn_wc__db_wcroot_t *wcroot,
11807                 const char *local_relpath,
11808                 apr_pool_t *scratch_pool)
11809 {
11810   const char *repos_relpath;
11811   apr_int64_t repos_id;
11812   svn_sqlite__stmt_t *stmt;
11813
11814   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11815                                             &repos_relpath, &repos_id,
11816                                             NULL, NULL, NULL, NULL, NULL,
11817                                             NULL, NULL, NULL, NULL, NULL,
11818                                             wcroot, local_relpath,
11819                                             scratch_pool, scratch_pool));
11820
11821   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11822                                     STMT_DELETE_LOCK));
11823   SVN_ERR(svn_sqlite__bindf(stmt, "is", repos_id, repos_relpath));
11824
11825   SVN_ERR(svn_sqlite__step_done(stmt));
11826
11827   return SVN_NO_ERROR;
11828 }
11829
11830
11831 svn_error_t *
11832 svn_wc__db_lock_remove(svn_wc__db_t *db,
11833                        const char *local_abspath,
11834                        apr_pool_t *scratch_pool)
11835 {
11836   svn_wc__db_wcroot_t *wcroot;
11837   const char *local_relpath;
11838
11839   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11840
11841   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11842                               local_abspath, scratch_pool, scratch_pool));
11843   VERIFY_USABLE_WCROOT(wcroot);
11844
11845   SVN_WC__DB_WITH_TXN(
11846     lock_remove_txn(wcroot, local_relpath, scratch_pool),
11847     wcroot);
11848
11849   /* There may be some entries, and the lock info is now out of date.  */
11850   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
11851
11852   return SVN_NO_ERROR;
11853 }
11854
11855
11856 svn_error_t *
11857 svn_wc__db_scan_base_repos(const char **repos_relpath,
11858                            const char **repos_root_url,
11859                            const char **repos_uuid,
11860                            svn_wc__db_t *db,
11861                            const char *local_abspath,
11862                            apr_pool_t *result_pool,
11863                            apr_pool_t *scratch_pool)
11864 {
11865   svn_wc__db_wcroot_t *wcroot;
11866   const char *local_relpath;
11867   apr_int64_t repos_id;
11868
11869   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11870
11871   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11872                               local_abspath, scratch_pool, scratch_pool));
11873   VERIFY_USABLE_WCROOT(wcroot);
11874
11875   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
11876                                             repos_relpath, &repos_id,
11877                                             NULL, NULL, NULL, NULL, NULL,
11878                                             NULL, NULL, NULL, NULL, NULL,
11879                                             wcroot, local_relpath,
11880                                             result_pool, scratch_pool));
11881   SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot->sdb,
11882                                       repos_id, result_pool));
11883
11884   return SVN_NO_ERROR;
11885 }
11886
11887
11888 /* A helper for scan_addition().
11889  * Compute moved-from information for the node at LOCAL_RELPATH which
11890  * has been determined as having been moved-here.
11891  * If MOVED_FROM_RELPATH is not NULL, set *MOVED_FROM_RELPATH to the
11892  * path of the move-source node in *MOVED_FROM_RELPATH.
11893  * If DELETE_OP_ROOT_RELPATH is not NULL, set *DELETE_OP_ROOT_RELPATH
11894  * to the path of the op-root of the delete-half of the move.
11895  * If moved-from information cannot be derived, set both *MOVED_FROM_RELPATH
11896  * and *DELETE_OP_ROOT_RELPATH to NULL, and return a "copied" status.
11897  * COPY_OPT_ROOT_RELPATH is the relpath of the op-root of the copied-half
11898  * of the move. */
11899 static svn_error_t *
11900 get_moved_from_info(const char **moved_from_relpath,
11901                     const char **moved_from_op_root_relpath,
11902                     const char *moved_to_op_root_relpath,
11903                     int *op_depth,
11904                     svn_wc__db_wcroot_t *wcroot,
11905                     const char *local_relpath,
11906                     apr_pool_t *result_pool,
11907                     apr_pool_t *scratch_pool)
11908 {
11909   svn_sqlite__stmt_t *stmt;
11910   svn_boolean_t have_row;
11911
11912   /* Run a query to get the moved-from path from the DB. */
11913   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11914                                     STMT_SELECT_MOVED_FROM_RELPATH));
11915   SVN_ERR(svn_sqlite__bindf(stmt, "is",
11916                             wcroot->wc_id, moved_to_op_root_relpath));
11917   SVN_ERR(svn_sqlite__step(&have_row, stmt));
11918
11919   if (!have_row)
11920     {
11921       /* The move was only recorded at the copy-half, possibly because
11922        * the move operation was interrupted mid-way between the copy
11923        * and the delete. Treat this node as a normal copy. */
11924       if (moved_from_relpath)
11925         *moved_from_relpath = NULL;
11926       if (moved_from_op_root_relpath)
11927         *moved_from_op_root_relpath = NULL;
11928
11929       SVN_ERR(svn_sqlite__reset(stmt));
11930       return SVN_NO_ERROR;
11931     }
11932
11933   if (op_depth)
11934     *op_depth = svn_sqlite__column_int(stmt, 1);
11935
11936   if (moved_from_relpath || moved_from_op_root_relpath)
11937     {
11938       const char *db_delete_op_root_relpath;
11939
11940       /* The moved-from path from the DB is the relpath of
11941        * the op_root of the delete-half of the move. */
11942       db_delete_op_root_relpath = svn_sqlite__column_text(stmt, 0,
11943                                                           result_pool);
11944       if (moved_from_op_root_relpath)
11945         *moved_from_op_root_relpath = db_delete_op_root_relpath;
11946
11947       if (moved_from_relpath)
11948         {
11949           if (strcmp(moved_to_op_root_relpath, local_relpath) == 0)
11950             {
11951               /* LOCAL_RELPATH is the op_root of the copied-half of the
11952                * move, so the correct MOVED_FROM_ABSPATH is the op-root
11953                * of the delete-half. */
11954               *moved_from_relpath = db_delete_op_root_relpath;
11955             }
11956           else
11957             {
11958               const char *child_relpath;
11959
11960               /* LOCAL_RELPATH is a child that was copied along with the
11961                * op_root of the copied-half of the move. Construct the
11962                * corresponding path beneath the op_root of the delete-half. */
11963
11964               /* Grab the child path relative to the op_root of the move
11965                * destination. */
11966               child_relpath = svn_relpath_skip_ancestor(
11967                                 moved_to_op_root_relpath, local_relpath);
11968
11969               SVN_ERR_ASSERT(child_relpath && strlen(child_relpath) > 0);
11970
11971               /* This join is valid because LOCAL_RELPATH has not been moved
11972                * within the copied-half of the move yet -- else, it would
11973                * be its own op_root. */
11974               *moved_from_relpath = svn_relpath_join(db_delete_op_root_relpath,
11975                                                      child_relpath,
11976                                                      result_pool);
11977             }
11978         }
11979     }
11980
11981   SVN_ERR(svn_sqlite__reset(stmt));
11982
11983   return SVN_NO_ERROR;
11984 }
11985
11986 /* The body of scan_addition().
11987  */
11988 static svn_error_t *
11989 scan_addition_txn(svn_wc__db_status_t *status,
11990                   const char **op_root_relpath_p,
11991                   const char **repos_relpath,
11992                   apr_int64_t *repos_id,
11993                   const char **original_repos_relpath,
11994                   apr_int64_t *original_repos_id,
11995                   svn_revnum_t *original_revision,
11996                   const char **moved_from_relpath,
11997                   const char **moved_from_op_root_relpath,
11998                   int *moved_from_op_depth,
11999                   svn_wc__db_wcroot_t *wcroot,
12000                   const char *local_relpath,
12001                   apr_pool_t *result_pool,
12002                   apr_pool_t *scratch_pool)
12003 {
12004   const char *op_root_relpath;
12005   const char *build_relpath = "";
12006
12007   /* Initialize most of the OUT parameters. Generally, we'll only be filling
12008      in a subset of these, so it is easier to init all up front. Note that
12009      the STATUS parameter will be initialized once we read the status of
12010      the specified node.  */
12011   if (op_root_relpath_p)
12012     *op_root_relpath_p = NULL;
12013   if (original_repos_relpath)
12014     *original_repos_relpath = NULL;
12015   if (original_repos_id)
12016     *original_repos_id = INVALID_REPOS_ID;
12017   if (original_revision)
12018     *original_revision = SVN_INVALID_REVNUM;
12019   if (moved_from_relpath)
12020     *moved_from_relpath = NULL;
12021   if (moved_from_op_root_relpath)
12022     *moved_from_op_root_relpath = NULL;
12023   if (moved_from_op_depth)
12024     *moved_from_op_depth = 0;
12025
12026   {
12027     svn_sqlite__stmt_t *stmt;
12028     svn_boolean_t have_row;
12029     svn_wc__db_status_t presence;
12030     int op_depth;
12031     const char *repos_prefix_path = "";
12032     int i;
12033
12034     /* ### is it faster to fetch fewer columns? */
12035     SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12036                                       STMT_SELECT_WORKING_NODE));
12037     SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
12038     SVN_ERR(svn_sqlite__step(&have_row, stmt));
12039
12040     if (!have_row)
12041       {
12042         /* Reset statement before returning */
12043         SVN_ERR(svn_sqlite__reset(stmt));
12044
12045         /* ### maybe we should return a usage error instead?  */
12046         return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12047                                  _("The node '%s' was not found."),
12048                                  path_for_error_message(wcroot,
12049                                                         local_relpath,
12050                                                         scratch_pool));
12051       }
12052
12053     presence = svn_sqlite__column_token(stmt, 1, presence_map);
12054
12055     /* The starting node should exist normally.  */
12056     op_depth = svn_sqlite__column_int(stmt, 0);
12057     if (op_depth == 0 || (presence != svn_wc__db_status_normal
12058                           && presence != svn_wc__db_status_incomplete))
12059       /* reset the statement as part of the error generation process */
12060       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
12061                                svn_sqlite__reset(stmt),
12062                                _("Expected node '%s' to be added."),
12063                                path_for_error_message(wcroot,
12064                                                       local_relpath,
12065                                                       scratch_pool));
12066
12067     if (original_revision)
12068       *original_revision = svn_sqlite__column_revnum(stmt, 12);
12069
12070     /* Provide the default status; we'll override as appropriate. */
12071     if (status)
12072       {
12073         if (presence == svn_wc__db_status_normal)
12074           *status = svn_wc__db_status_added;
12075         else
12076           *status = svn_wc__db_status_incomplete;
12077       }
12078
12079
12080     /* Calculate the op root local path components */
12081     op_root_relpath = local_relpath;
12082
12083     for (i = relpath_depth(local_relpath); i > op_depth; --i)
12084       {
12085         /* Calculate the path of the operation root */
12086         repos_prefix_path =
12087           svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
12088                            repos_prefix_path,
12089                            scratch_pool);
12090         op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
12091       }
12092
12093     if (op_root_relpath_p)
12094       *op_root_relpath_p = apr_pstrdup(result_pool, op_root_relpath);
12095
12096     /* ### This if-statement is quite redundant.
12097      * ### We're checking all these values again within the body anyway.
12098      * ### The body should be broken up appropriately and move into the
12099      * ### outer scope. */
12100     if (original_repos_relpath
12101         || original_repos_id
12102         || (original_revision
12103                 && *original_revision == SVN_INVALID_REVNUM)
12104         || status
12105         || moved_from_relpath || moved_from_op_root_relpath)
12106       {
12107         if (local_relpath != op_root_relpath)
12108           /* requery to get the add/copy root */
12109           {
12110             SVN_ERR(svn_sqlite__reset(stmt));
12111
12112             SVN_ERR(svn_sqlite__bindf(stmt, "is",
12113                                       wcroot->wc_id, op_root_relpath));
12114             SVN_ERR(svn_sqlite__step(&have_row, stmt));
12115
12116             if (!have_row)
12117               {
12118                 /* Reset statement before returning */
12119                 SVN_ERR(svn_sqlite__reset(stmt));
12120
12121                 /* ### maybe we should return a usage error instead?  */
12122                 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12123                                          _("The node '%s' was not found."),
12124                                          path_for_error_message(wcroot,
12125                                                                 op_root_relpath,
12126                                                                 scratch_pool));
12127               }
12128
12129             if (original_revision
12130                     && *original_revision == SVN_INVALID_REVNUM)
12131               *original_revision = svn_sqlite__column_revnum(stmt, 12);
12132           }
12133
12134         if (original_repos_relpath)
12135           *original_repos_relpath = svn_sqlite__column_text(stmt, 11,
12136                                                             result_pool);
12137
12138         if (!svn_sqlite__column_is_null(stmt, 10)
12139             && (status
12140                 || original_repos_id
12141                 || moved_from_relpath || moved_from_op_root_relpath))
12142           /* If column 10 (original_repos_id) is NULL,
12143              this is a plain add, not a copy or a move */
12144           {
12145             svn_boolean_t moved_here;
12146             if (original_repos_id)
12147               *original_repos_id = svn_sqlite__column_int64(stmt, 10);
12148
12149             moved_here = svn_sqlite__column_boolean(stmt, 13 /* moved_here */);
12150             if (status)
12151               *status = moved_here ? svn_wc__db_status_moved_here
12152                                    : svn_wc__db_status_copied;
12153
12154             if (moved_here
12155                 && (moved_from_relpath || moved_from_op_root_relpath))
12156               {
12157                 svn_error_t *err;
12158
12159                 err = get_moved_from_info(moved_from_relpath,
12160                                           moved_from_op_root_relpath,
12161                                           op_root_relpath,
12162                                           moved_from_op_depth,
12163                                           wcroot, local_relpath,
12164                                           result_pool,
12165                                           scratch_pool);
12166
12167                 if (err)
12168                   return svn_error_compose_create(
12169                                 err, svn_sqlite__reset(stmt));
12170               }
12171           }
12172       }
12173
12174
12175     /* ### This loop here is to skip up to the first node which is a BASE node,
12176        because base_get_info() doesn't accommodate the scenario that
12177        we're looking at here; we found the true op_root, which may be inside
12178        further changed trees. */
12179     if (repos_relpath || repos_id)
12180       {
12181         const char *base_relpath;
12182
12183     while (TRUE)
12184       {
12185
12186         SVN_ERR(svn_sqlite__reset(stmt));
12187
12188         /* Pointing at op_depth, look at the parent */
12189         repos_prefix_path =
12190           svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
12191                            repos_prefix_path,
12192                            scratch_pool);
12193         op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
12194
12195
12196         SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, op_root_relpath));
12197         SVN_ERR(svn_sqlite__step(&have_row, stmt));
12198
12199         if (! have_row)
12200           break;
12201
12202         op_depth = svn_sqlite__column_int(stmt, 0);
12203
12204         /* Skip to op_depth */
12205         for (i = relpath_depth(op_root_relpath); i > op_depth; i--)
12206           {
12207             /* Calculate the path of the operation root */
12208             repos_prefix_path =
12209               svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
12210                                repos_prefix_path,
12211                                scratch_pool);
12212             op_root_relpath =
12213               svn_relpath_dirname(op_root_relpath, scratch_pool);
12214           }
12215       }
12216
12217       SVN_ERR(svn_sqlite__reset(stmt));
12218
12219       build_relpath = repos_prefix_path;
12220
12221       /* If we're here, then we have an added/copied/moved (start) node, and
12222          CURRENT_ABSPATH now points to a BASE node. Figure out the repository
12223          information for the current node, and use that to compute the start
12224          node's repository information.  */
12225       SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12226                                                 &base_relpath, repos_id,
12227                                                 NULL, NULL, NULL, NULL, NULL,
12228                                                 NULL, NULL, NULL, NULL, NULL,
12229                                                 wcroot, op_root_relpath,
12230                                                 scratch_pool, scratch_pool));
12231
12232         if (repos_relpath)
12233           *repos_relpath = svn_relpath_join(base_relpath, build_relpath,
12234                                             result_pool);
12235       }
12236     else
12237       SVN_ERR(svn_sqlite__reset(stmt));
12238   }
12239   /* Postconditions */
12240 #ifdef SVN_DEBUG
12241   if (status)
12242     {
12243       SVN_ERR_ASSERT(*status == svn_wc__db_status_added
12244                      || *status == svn_wc__db_status_copied
12245                      || *status == svn_wc__db_status_incomplete
12246                      || *status == svn_wc__db_status_moved_here);
12247       if (*status == svn_wc__db_status_added)
12248         {
12249           SVN_ERR_ASSERT(!original_repos_relpath
12250                          || *original_repos_relpath == NULL);
12251           SVN_ERR_ASSERT(!original_revision
12252                          || *original_revision == SVN_INVALID_REVNUM);
12253           SVN_ERR_ASSERT(!original_repos_id
12254                          || *original_repos_id == INVALID_REPOS_ID);
12255         }
12256       /* An upgrade with a missing directory can leave INCOMPLETE working
12257          op-roots. See upgrade_tests.py 29: upgrade with missing replaced dir
12258        */
12259       else if (*status != svn_wc__db_status_incomplete)
12260         {
12261           SVN_ERR_ASSERT(!original_repos_relpath
12262                          || *original_repos_relpath != NULL);
12263           SVN_ERR_ASSERT(!original_revision
12264                          || *original_revision != SVN_INVALID_REVNUM);
12265           SVN_ERR_ASSERT(!original_repos_id
12266                          || *original_repos_id != INVALID_REPOS_ID);
12267         }
12268     }
12269   SVN_ERR_ASSERT(!op_root_relpath_p || *op_root_relpath_p != NULL);
12270 #endif
12271
12272   return SVN_NO_ERROR;
12273 }
12274
12275
12276 /* Like svn_wc__db_scan_addition(), but with WCROOT+LOCAL_RELPATH instead of
12277    DB+LOCAL_ABSPATH.
12278
12279    The output value of *ORIGINAL_REPOS_ID will be INVALID_REPOS_ID if there
12280    is no 'copy-from' repository.  */
12281 static svn_error_t *
12282 scan_addition(svn_wc__db_status_t *status,
12283               const char **op_root_relpath,
12284               const char **repos_relpath,
12285               apr_int64_t *repos_id,
12286               const char **original_repos_relpath,
12287               apr_int64_t *original_repos_id,
12288               svn_revnum_t *original_revision,
12289               const char **moved_from_relpath,
12290               const char **moved_from_op_root_relpath,
12291               int *moved_from_op_depth,
12292               svn_wc__db_wcroot_t *wcroot,
12293               const char *local_relpath,
12294               apr_pool_t *result_pool,
12295               apr_pool_t *scratch_pool)
12296 {
12297   SVN_WC__DB_WITH_TXN(
12298     scan_addition_txn(status, op_root_relpath, repos_relpath, repos_id,
12299                       original_repos_relpath, original_repos_id,
12300                       original_revision, moved_from_relpath,
12301                       moved_from_op_root_relpath, moved_from_op_depth,
12302                       wcroot, local_relpath, result_pool, scratch_pool),
12303     wcroot);
12304   return SVN_NO_ERROR;
12305 }
12306
12307
12308 svn_error_t *
12309 svn_wc__db_scan_addition(svn_wc__db_status_t *status,
12310                          const char **op_root_abspath,
12311                          const char **repos_relpath,
12312                          const char **repos_root_url,
12313                          const char **repos_uuid,
12314                          const char **original_repos_relpath,
12315                          const char **original_root_url,
12316                          const char **original_uuid,
12317                          svn_revnum_t *original_revision,
12318                          svn_wc__db_t *db,
12319                          const char *local_abspath,
12320                          apr_pool_t *result_pool,
12321                          apr_pool_t *scratch_pool)
12322 {
12323   svn_wc__db_wcroot_t *wcroot;
12324   const char *local_relpath;
12325   const char *op_root_relpath = NULL;
12326   apr_int64_t repos_id = INVALID_REPOS_ID;
12327   apr_int64_t original_repos_id = INVALID_REPOS_ID;
12328   apr_int64_t *repos_id_p
12329     = (repos_root_url || repos_uuid) ? &repos_id : NULL;
12330   apr_int64_t *original_repos_id_p
12331     = (original_root_url || original_uuid) ? &original_repos_id : NULL;
12332
12333   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12334
12335   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12336                               local_abspath, scratch_pool, scratch_pool));
12337   VERIFY_USABLE_WCROOT(wcroot);
12338
12339   SVN_ERR(scan_addition(status,
12340                         op_root_abspath
12341                                 ? &op_root_relpath
12342                                 : NULL,
12343                         repos_relpath, repos_id_p,
12344                         original_repos_relpath, original_repos_id_p,
12345                         original_revision,
12346                         NULL, NULL, NULL,
12347                         wcroot, local_relpath, result_pool, scratch_pool));
12348
12349   if (op_root_abspath)
12350     *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
12351                                        result_pool);
12352   /* REPOS_ID must be valid if requested; ORIGINAL_REPOS_ID need not be. */
12353   SVN_ERR_ASSERT(repos_id_p == NULL || repos_id != INVALID_REPOS_ID);
12354
12355   SVN_ERR(svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot->sdb,
12356                                       repos_id, result_pool));
12357   SVN_ERR(svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
12358                                       wcroot->sdb, original_repos_id,
12359                                       result_pool));
12360
12361   return SVN_NO_ERROR;
12362 }
12363
12364 svn_error_t *
12365 svn_wc__db_scan_moved(const char **moved_from_abspath,
12366                       const char **op_root_abspath,
12367                       const char **op_root_moved_from_abspath,
12368                       const char **moved_from_delete_abspath,
12369                       svn_wc__db_t *db,
12370                       const char *local_abspath,
12371                       apr_pool_t *result_pool,
12372                       apr_pool_t *scratch_pool)
12373 {
12374   svn_wc__db_wcroot_t *wcroot;
12375   const char *local_relpath;
12376   svn_wc__db_status_t status;
12377   const char *op_root_relpath = NULL;
12378   const char *moved_from_relpath = NULL;
12379   const char *moved_from_op_root_relpath = NULL;
12380   int moved_from_op_depth = -1;
12381
12382   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12383
12384   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12385                               local_abspath, scratch_pool, scratch_pool));
12386   VERIFY_USABLE_WCROOT(wcroot);
12387
12388   SVN_ERR(scan_addition(&status,
12389                         op_root_abspath
12390                                 ? &op_root_relpath
12391                                 : NULL,
12392                         NULL, NULL,
12393                         NULL, NULL, NULL,
12394                         moved_from_abspath
12395                             ? &moved_from_relpath
12396                             : NULL,
12397                         (op_root_moved_from_abspath
12398                          || moved_from_delete_abspath)
12399                             ? &moved_from_op_root_relpath
12400                             : NULL,
12401                         moved_from_delete_abspath
12402                             ? &moved_from_op_depth
12403                             : NULL,
12404                         wcroot, local_relpath, scratch_pool, scratch_pool));
12405
12406   if (status != svn_wc__db_status_moved_here || !moved_from_relpath)
12407     return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
12408                              _("Path '%s' was not moved here"),
12409                              path_for_error_message(wcroot, local_relpath,
12410                                                     scratch_pool));
12411
12412   if (op_root_abspath)
12413     *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
12414                                        result_pool);
12415
12416   if (moved_from_abspath)
12417     *moved_from_abspath = svn_dirent_join(wcroot->abspath, moved_from_relpath,
12418                                           result_pool);
12419
12420   if (op_root_moved_from_abspath)
12421     *op_root_moved_from_abspath = svn_dirent_join(wcroot->abspath,
12422                                                   moved_from_op_root_relpath,
12423                                                   result_pool);
12424
12425   /* The deleted node is either where we moved from, or one of its ancestors */
12426   if (moved_from_delete_abspath)
12427     {
12428       const char *tmp = moved_from_op_root_relpath;
12429
12430       SVN_ERR_ASSERT(moved_from_op_depth >= 0);
12431
12432       while (relpath_depth(tmp) > moved_from_op_depth)
12433         tmp = svn_relpath_dirname(tmp, scratch_pool);
12434
12435       *moved_from_delete_abspath = svn_dirent_join(wcroot->abspath, tmp,
12436                                                    scratch_pool);
12437     }
12438
12439   return SVN_NO_ERROR;
12440 }
12441
12442 /* ###
12443  */
12444 static svn_error_t *
12445 follow_moved_to(apr_array_header_t **moved_tos,
12446                 int op_depth,
12447                 const char *repos_path,
12448                 svn_revnum_t revision,
12449                 svn_wc__db_wcroot_t *wcroot,
12450                 const char *local_relpath,
12451                 apr_pool_t *result_pool,
12452                 apr_pool_t *scratch_pool)
12453 {
12454   svn_sqlite__stmt_t *stmt;
12455   svn_boolean_t have_row;
12456   int working_op_depth;
12457   const char *ancestor_relpath, *node_moved_to = NULL;
12458   int i;
12459
12460   SVN_ERR_ASSERT((!op_depth && !repos_path) || (op_depth && repos_path));
12461
12462   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12463                                     STMT_SELECT_OP_DEPTH_MOVED_TO));
12464   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
12465                             op_depth));
12466   SVN_ERR(svn_sqlite__step(&have_row, stmt));
12467   if (have_row)
12468     {
12469       working_op_depth = svn_sqlite__column_int(stmt, 0);
12470       node_moved_to = svn_sqlite__column_text(stmt, 1, result_pool);
12471       if (!repos_path)
12472         {
12473           SVN_ERR(svn_sqlite__step(&have_row, stmt));
12474           if (!have_row || svn_sqlite__column_revnum(stmt, 0))
12475             return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
12476                                      svn_sqlite__reset(stmt),
12477                                      _("The base node '%s' was not found."),
12478                                      path_for_error_message(wcroot,
12479                                                             local_relpath,
12480                                                             scratch_pool));
12481           repos_path = svn_sqlite__column_text(stmt, 2, scratch_pool);
12482           revision = svn_sqlite__column_revnum(stmt, 3);
12483         }
12484     }
12485   SVN_ERR(svn_sqlite__reset(stmt));
12486
12487   if (node_moved_to)
12488     {
12489       svn_boolean_t have_row2;
12490
12491       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12492                                         STMT_SELECT_MOVED_HERE));
12493       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, node_moved_to,
12494                                 relpath_depth(node_moved_to)));
12495       SVN_ERR(svn_sqlite__step(&have_row2, stmt));
12496       if (!have_row2 || !svn_sqlite__column_int(stmt, 0)
12497           || revision != svn_sqlite__column_revnum(stmt, 3)
12498           || strcmp(repos_path, svn_sqlite__column_text(stmt, 2, NULL)))
12499         node_moved_to = NULL;
12500       SVN_ERR(svn_sqlite__reset(stmt));
12501     }
12502
12503   if (node_moved_to)
12504     {
12505       struct svn_wc__db_moved_to_t *moved_to;
12506
12507       moved_to = apr_palloc(result_pool, sizeof(*moved_to));
12508       moved_to->op_depth = working_op_depth;
12509       moved_to->local_relpath = node_moved_to;
12510       APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
12511     }
12512
12513   /* A working row with moved_to, or no working row, and we are done. */
12514   if (node_moved_to || !have_row)
12515     return SVN_NO_ERROR;
12516
12517   /* Need to handle being moved via an ancestor. */
12518   ancestor_relpath = local_relpath;
12519   for (i = relpath_depth(local_relpath); i > working_op_depth; --i)
12520     {
12521       const char *ancestor_moved_to;
12522
12523       ancestor_relpath = svn_relpath_dirname(ancestor_relpath, scratch_pool);
12524
12525       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12526                                         STMT_SELECT_MOVED_TO));
12527       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, ancestor_relpath,
12528                                 working_op_depth));
12529       SVN_ERR(svn_sqlite__step(&have_row, stmt));
12530       SVN_ERR_ASSERT(have_row);
12531       ancestor_moved_to = svn_sqlite__column_text(stmt, 0, scratch_pool);
12532       SVN_ERR(svn_sqlite__reset(stmt));
12533       if (ancestor_moved_to)
12534         {
12535           node_moved_to
12536             = svn_relpath_join(ancestor_moved_to,
12537                                svn_relpath_skip_ancestor(ancestor_relpath,
12538                                                          local_relpath),
12539                                result_pool);
12540
12541           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12542                                             STMT_SELECT_MOVED_HERE));
12543           SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, node_moved_to,
12544                                     relpath_depth(ancestor_moved_to)));
12545           SVN_ERR(svn_sqlite__step(&have_row, stmt));
12546           if (!have_row)
12547             ancestor_moved_to = NULL;
12548           else if (!svn_sqlite__column_int(stmt, 0))
12549             {
12550               svn_wc__db_status_t presence
12551                 = svn_sqlite__column_token(stmt, 1, presence_map);
12552               if (presence != svn_wc__db_status_not_present)
12553                 ancestor_moved_to = NULL;
12554               else
12555                 {
12556                   SVN_ERR(svn_sqlite__step(&have_row, stmt));
12557                   if (!have_row && !svn_sqlite__column_int(stmt, 0))
12558                     ancestor_moved_to = NULL;
12559                 }
12560             }
12561           SVN_ERR(svn_sqlite__reset(stmt));
12562           if (!ancestor_moved_to)
12563             break;
12564           /* verify repos_path points back? */
12565         }
12566       if (ancestor_moved_to)
12567         {
12568           struct svn_wc__db_moved_to_t *moved_to;
12569
12570           moved_to = apr_palloc(result_pool, sizeof(*moved_to));
12571           moved_to->op_depth = working_op_depth;
12572           moved_to->local_relpath = node_moved_to;
12573           APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
12574
12575           SVN_ERR(follow_moved_to(moved_tos, relpath_depth(ancestor_moved_to),
12576                                   repos_path, revision, wcroot, node_moved_to,
12577                                   result_pool, scratch_pool));
12578           break;
12579         }
12580     }
12581
12582   return SVN_NO_ERROR;
12583 }
12584
12585 svn_error_t *
12586 svn_wc__db_follow_moved_to(apr_array_header_t **moved_tos,
12587                            svn_wc__db_t *db,
12588                            const char *local_abspath,
12589                            apr_pool_t *result_pool,
12590                            apr_pool_t *scratch_pool)
12591 {
12592   svn_wc__db_wcroot_t *wcroot;
12593   const char *local_relpath;
12594
12595   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12596
12597   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12598                               local_abspath, scratch_pool, scratch_pool));
12599   VERIFY_USABLE_WCROOT(wcroot);
12600
12601   *moved_tos = apr_array_make(result_pool, 0,
12602                               sizeof(struct svn_wc__db_moved_to_t *));
12603
12604   /* ### Wrap in a transaction */
12605   SVN_ERR(follow_moved_to(moved_tos, 0, NULL, SVN_INVALID_REVNUM,
12606                           wcroot, local_relpath,
12607                           result_pool, scratch_pool));
12608
12609   /* ### Convert moved_to to abspath */
12610
12611   return SVN_NO_ERROR;
12612 }
12613
12614 /* Extract the moved-to information for LOCAL_RELPATH at OP-DEPTH by
12615    examining the lowest working node above OP_DEPTH.  The output paths
12616    are NULL if there is no move, otherwise:
12617
12618    *MOVE_DST_RELPATH: the moved-to destination of LOCAL_RELPATH.
12619
12620    *MOVE_DST_OP_ROOT_RELPATH: the moved-to destination of the root of
12621    the move of LOCAL_RELPATH. This may be equal to *MOVE_DST_RELPATH
12622    if LOCAL_RELPATH is the root of the move.
12623
12624    *MOVE_SRC_ROOT_RELPATH: the root of the move source.  For moves
12625    inside a delete this will be different from *MOVE_SRC_OP_ROOT_RELPATH.
12626
12627    *MOVE_SRC_OP_ROOT_RELPATH: the root of the source layer that
12628    contains the move.  For moves inside deletes this is the root of
12629    the delete, for other moves this is the root of the move.
12630
12631    Given a path A/B/C with A/B moved to X then for A/B/C
12632
12633      MOVE_DST_RELPATH is X/C
12634      MOVE_DST_OP_ROOT_RELPATH is X
12635      MOVE_SRC_ROOT_RELPATH is A/B
12636      MOVE_SRC_OP_ROOT_RELPATH is A/B
12637
12638    If A is then deleted the MOVE_DST_RELPATH, MOVE_DST_OP_ROOT_RELPATH
12639    and MOVE_SRC_ROOT_RELPATH remain the same but MOVE_SRC_OP_ROOT_RELPATH
12640    changes to A.
12641
12642    ### Think about combining with scan_deletion?  Also with
12643    ### scan_addition to get moved-to for replaces?  Do we need to
12644    ### return the op-root of the move source, i.e. A/B in the example
12645    ### above?  */
12646 svn_error_t *
12647 svn_wc__db_op_depth_moved_to(const char **move_dst_relpath,
12648                              const char **move_dst_op_root_relpath,
12649                              const char **move_src_root_relpath,
12650                              const char **move_src_op_root_relpath,
12651                              int op_depth,
12652                              svn_wc__db_wcroot_t *wcroot,
12653                              const char *local_relpath,
12654                              apr_pool_t *result_pool,
12655                              apr_pool_t *scratch_pool)
12656 {
12657   svn_sqlite__stmt_t *stmt;
12658   svn_boolean_t have_row;
12659   int delete_op_depth;
12660   const char *relpath = local_relpath;
12661
12662   *move_dst_relpath = *move_dst_op_root_relpath = NULL;
12663   *move_src_root_relpath = *move_src_op_root_relpath = NULL;
12664
12665   do
12666     {
12667       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12668                                         STMT_SELECT_LOWEST_WORKING_NODE));
12669       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath, op_depth));
12670       SVN_ERR(svn_sqlite__step(&have_row, stmt));
12671       if (have_row)
12672         {
12673           delete_op_depth = svn_sqlite__column_int(stmt, 0);
12674           *move_dst_op_root_relpath = svn_sqlite__column_text(stmt, 3,
12675                                                               result_pool);
12676           if (*move_dst_op_root_relpath)
12677             *move_src_root_relpath = apr_pstrdup(result_pool, relpath);
12678         }
12679       SVN_ERR(svn_sqlite__reset(stmt));
12680       if (!*move_dst_op_root_relpath)
12681         relpath = svn_relpath_dirname(relpath, scratch_pool);
12682     }
12683   while (!*move_dst_op_root_relpath
12684         && have_row && delete_op_depth <= relpath_depth(relpath));
12685
12686   if (*move_dst_op_root_relpath)
12687     {
12688       *move_dst_relpath
12689         = svn_relpath_join(*move_dst_op_root_relpath,
12690                            svn_relpath_skip_ancestor(relpath, local_relpath),
12691                            result_pool);
12692       while (delete_op_depth < relpath_depth(relpath))
12693         relpath = svn_relpath_dirname(relpath, scratch_pool);
12694       *move_src_op_root_relpath = apr_pstrdup(result_pool, relpath);
12695     }
12696
12697   return SVN_NO_ERROR;
12698 }
12699
12700 /* Public (within libsvn_wc) absolute path version of
12701    svn_wc__db_op_depth_moved_to with the op-depth hard-coded to
12702    BASE. */
12703 svn_error_t *
12704 svn_wc__db_base_moved_to(const char **move_dst_abspath,
12705                          const char **move_dst_op_root_abspath,
12706                          const char **move_src_root_abspath,
12707                          const char **move_src_op_root_abspath,
12708                          svn_wc__db_t *db,
12709                          const char *local_abspath,
12710                          apr_pool_t *result_pool,
12711                          apr_pool_t *scratch_pool)
12712 {
12713   svn_wc__db_wcroot_t *wcroot;
12714   const char *local_relpath;
12715   const char *move_dst_relpath, *move_dst_op_root_relpath;
12716   const char *move_src_root_relpath, *move_src_op_root_relpath;
12717
12718   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12719
12720   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12721                               local_abspath, scratch_pool, scratch_pool));
12722   VERIFY_USABLE_WCROOT(wcroot);
12723
12724   SVN_WC__DB_WITH_TXN(svn_wc__db_op_depth_moved_to(&move_dst_relpath,
12725                                                    &move_dst_op_root_relpath,
12726                                                    &move_src_root_relpath,
12727                                                    &move_src_op_root_relpath,
12728                                                    0 /* BASE op-depth */,
12729                                                    wcroot, local_relpath,
12730                                                    scratch_pool, scratch_pool),
12731                       wcroot);
12732
12733   if (move_dst_abspath)
12734     *move_dst_abspath
12735       = move_dst_relpath
12736       ? svn_dirent_join(wcroot->abspath, move_dst_relpath, result_pool)
12737       : NULL;
12738
12739   if (move_dst_op_root_abspath)
12740     *move_dst_op_root_abspath
12741       = move_dst_op_root_relpath
12742       ? svn_dirent_join(wcroot->abspath, move_dst_op_root_relpath, result_pool)
12743       : NULL;
12744
12745   if (move_src_root_abspath)
12746     *move_src_root_abspath
12747       = move_src_root_relpath
12748       ? svn_dirent_join(wcroot->abspath, move_src_root_relpath, result_pool)
12749       : NULL;
12750
12751   if (move_src_op_root_abspath)
12752     *move_src_op_root_abspath
12753       = move_src_op_root_relpath
12754       ? svn_dirent_join(wcroot->abspath, move_src_op_root_relpath, result_pool)
12755       : NULL;
12756
12757   return SVN_NO_ERROR;
12758 }
12759
12760 svn_error_t *
12761 svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb,
12762                          apr_int64_t *repos_id,
12763                          apr_int64_t *wc_id,
12764                          svn_wc__db_t *wc_db,
12765                          const char *dir_abspath,
12766                          const char *repos_root_url,
12767                          const char *repos_uuid,
12768                          apr_pool_t *scratch_pool)
12769 {
12770   svn_wc__db_wcroot_t *wcroot;
12771
12772   /* Upgrade is inherently exclusive so specify exclusive locking. */
12773   SVN_ERR(create_db(sdb, repos_id, wc_id, dir_abspath,
12774                     repos_root_url, repos_uuid,
12775                     SDB_FILE,
12776                     NULL, SVN_INVALID_REVNUM, svn_depth_unknown,
12777                     TRUE /* exclusive */,
12778                     wc_db->state_pool, scratch_pool));
12779
12780   SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
12781                                        apr_pstrdup(wc_db->state_pool,
12782                                                    dir_abspath),
12783                                        *sdb, *wc_id, FORMAT_FROM_SDB,
12784                                        FALSE /* auto-upgrade */,
12785                                        FALSE /* enforce_empty_wq */,
12786                                        wc_db->state_pool, scratch_pool));
12787
12788   /* The WCROOT is complete. Stash it into DB.  */
12789   svn_hash_sets(wc_db->dir_data, wcroot->abspath, wcroot);
12790
12791   return SVN_NO_ERROR;
12792 }
12793
12794
12795 svn_error_t *
12796 svn_wc__db_upgrade_apply_dav_cache(svn_sqlite__db_t *sdb,
12797                                    const char *dir_relpath,
12798                                    apr_hash_t *cache_values,
12799                                    apr_pool_t *scratch_pool)
12800 {
12801   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
12802   apr_int64_t wc_id;
12803   apr_hash_index_t *hi;
12804   svn_sqlite__stmt_t *stmt;
12805
12806   SVN_ERR(svn_wc__db_util_fetch_wc_id(&wc_id, sdb, iterpool));
12807
12808   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12809                                     STMT_UPDATE_BASE_NODE_DAV_CACHE));
12810
12811   /* Iterate over all the wcprops, writing each one to the wc_db. */
12812   for (hi = apr_hash_first(scratch_pool, cache_values);
12813        hi;
12814        hi = apr_hash_next(hi))
12815     {
12816       const char *name = svn__apr_hash_index_key(hi);
12817       apr_hash_t *props = svn__apr_hash_index_val(hi);
12818       const char *local_relpath;
12819
12820       svn_pool_clear(iterpool);
12821
12822       local_relpath = svn_relpath_join(dir_relpath, name, iterpool);
12823
12824       SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
12825       SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, iterpool));
12826       SVN_ERR(svn_sqlite__step_done(stmt));
12827     }
12828
12829   svn_pool_destroy(iterpool);
12830
12831   return SVN_NO_ERROR;
12832 }
12833
12834
12835 svn_error_t *
12836 svn_wc__db_upgrade_apply_props(svn_sqlite__db_t *sdb,
12837                                const char *dir_abspath,
12838                                const char *local_relpath,
12839                                apr_hash_t *base_props,
12840                                apr_hash_t *revert_props,
12841                                apr_hash_t *working_props,
12842                                int original_format,
12843                                apr_int64_t wc_id,
12844                                apr_pool_t *scratch_pool)
12845 {
12846   svn_sqlite__stmt_t *stmt;
12847   svn_boolean_t have_row;
12848   int top_op_depth = -1;
12849   int below_op_depth = -1;
12850   svn_wc__db_status_t top_presence;
12851   svn_wc__db_status_t below_presence;
12852   int affected_rows;
12853
12854   /* ### working_props: use set_props_txn.
12855      ### if working_props == NULL, then skip. what if they equal the
12856      ### pristine props? we should probably do the compare here.
12857      ###
12858      ### base props go into WORKING_NODE if avail, otherwise BASE.
12859      ###
12860      ### revert only goes into BASE. (and WORKING better be there!)
12861
12862      Prior to 1.4.0 (ORIGINAL_FORMAT < 8), REVERT_PROPS did not exist. If a
12863      file was deleted, then a copy (potentially with props) was disallowed
12864      and could not replace the deletion. An addition *could* be performed,
12865      but that would never bring its own props.
12866
12867      1.4.0 through 1.4.5 created the concept of REVERT_PROPS, but had a
12868      bug in svn_wc_add_repos_file2() whereby a copy-with-props did NOT
12869      construct a REVERT_PROPS if the target had no props. Thus, reverting
12870      the delete/copy would see no REVERT_PROPS to restore, leaving the
12871      props from the copy source intact, and appearing as if they are (now)
12872      the base props for the previously-deleted file. (wc corruption)
12873
12874      1.4.6 ensured that an empty REVERT_PROPS would be established at all
12875      times. See issue 2530, and r861670 as starting points.
12876
12877      We will use ORIGINAL_FORMAT and SVN_WC__NO_REVERT_FILES to determine
12878      the handling of our inputs, relative to the state of this node.
12879   */
12880
12881   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_NODE_INFO));
12882   SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id, local_relpath));
12883   SVN_ERR(svn_sqlite__step(&have_row, stmt));
12884   if (have_row)
12885     {
12886       top_op_depth = svn_sqlite__column_int(stmt, 0);
12887       top_presence = svn_sqlite__column_token(stmt, 3, presence_map);
12888       SVN_ERR(svn_sqlite__step(&have_row, stmt));
12889       if (have_row)
12890         {
12891           below_op_depth = svn_sqlite__column_int(stmt, 0);
12892           below_presence = svn_sqlite__column_token(stmt, 3, presence_map);
12893         }
12894     }
12895   SVN_ERR(svn_sqlite__reset(stmt));
12896
12897   /* Detect the buggy scenario described above. We cannot upgrade this
12898      working copy if we have no idea where BASE_PROPS should go.  */
12899   if (original_format > SVN_WC__NO_REVERT_FILES
12900       && revert_props == NULL
12901       && top_op_depth != -1
12902       && top_presence == svn_wc__db_status_normal
12903       && below_op_depth != -1
12904       && below_presence != svn_wc__db_status_not_present)
12905     {
12906       /* There should be REVERT_PROPS, so it appears that we just ran into
12907          the described bug. Sigh.  */
12908       return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
12909                                _("The properties of '%s' are in an "
12910                                  "indeterminate state and cannot be "
12911                                  "upgraded. See issue #2530."),
12912                                svn_dirent_local_style(
12913                                  svn_dirent_join(dir_abspath, local_relpath,
12914                                                  scratch_pool), scratch_pool));
12915     }
12916
12917   /* Need at least one row, or two rows if there are revert props */
12918   if (top_op_depth == -1
12919       || (below_op_depth == -1 && revert_props))
12920     return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
12921                              _("Insufficient NODES rows for '%s'"),
12922                              svn_dirent_local_style(
12923                                svn_dirent_join(dir_abspath, local_relpath,
12924                                                scratch_pool), scratch_pool));
12925
12926   /* one row, base props only: upper row gets base props
12927      two rows, base props only: lower row gets base props
12928      two rows, revert props only: lower row gets revert props
12929      two rows, base and revert props: upper row gets base, lower gets revert */
12930
12931
12932   if (revert_props || below_op_depth == -1)
12933     {
12934       SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12935                                         STMT_UPDATE_NODE_PROPS));
12936       SVN_ERR(svn_sqlite__bindf(stmt, "isd",
12937                                 wc_id, local_relpath, top_op_depth));
12938       SVN_ERR(svn_sqlite__bind_properties(stmt, 4, base_props, scratch_pool));
12939       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
12940
12941       SVN_ERR_ASSERT(affected_rows == 1);
12942     }
12943
12944   if (below_op_depth != -1)
12945     {
12946       apr_hash_t *props = revert_props ? revert_props : base_props;
12947
12948       SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
12949                                         STMT_UPDATE_NODE_PROPS));
12950       SVN_ERR(svn_sqlite__bindf(stmt, "isd",
12951                                 wc_id, local_relpath, below_op_depth));
12952       SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
12953       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
12954
12955       SVN_ERR_ASSERT(affected_rows == 1);
12956     }
12957
12958   /* If there are WORKING_PROPS, then they always go into ACTUAL_NODE.  */
12959   if (working_props != NULL
12960       && base_props != NULL)
12961     {
12962       apr_array_header_t *diffs;
12963
12964       SVN_ERR(svn_prop_diffs(&diffs, working_props, base_props, scratch_pool));
12965
12966       if (diffs->nelts == 0)
12967         working_props = NULL; /* No differences */
12968     }
12969
12970   if (working_props != NULL)
12971     {
12972       SVN_ERR(set_actual_props(wc_id, local_relpath, working_props,
12973                                sdb, scratch_pool));
12974     }
12975
12976   return SVN_NO_ERROR;
12977 }
12978
12979 svn_error_t *
12980 svn_wc__db_upgrade_insert_external(svn_wc__db_t *db,
12981                                    const char *local_abspath,
12982                                    svn_node_kind_t kind,
12983                                    const char *parent_abspath,
12984                                    const char *def_local_abspath,
12985                                    const char *repos_relpath,
12986                                    const char *repos_root_url,
12987                                    const char *repos_uuid,
12988                                    svn_revnum_t def_peg_revision,
12989                                    svn_revnum_t def_revision,
12990                                    apr_pool_t *scratch_pool)
12991 {
12992   svn_wc__db_wcroot_t *wcroot;
12993   const char *def_local_relpath;
12994   svn_sqlite__stmt_t *stmt;
12995   svn_boolean_t have_row;
12996   apr_int64_t repos_id;
12997
12998   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12999
13000   /* We know only of DEF_LOCAL_ABSPATH that it definitely belongs to "this"
13001    * WC, i.e. where the svn:externals prop is set. The external target path
13002    * itself may be "hidden behind" other working copies. */
13003   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &def_local_relpath,
13004                                                 db, def_local_abspath,
13005                                                 scratch_pool, scratch_pool));
13006   VERIFY_USABLE_WCROOT(wcroot);
13007
13008
13009   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13010                                     STMT_SELECT_REPOSITORY));
13011   SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
13012   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13013
13014   if (have_row)
13015     repos_id = svn_sqlite__column_int64(stmt, 0);
13016   SVN_ERR(svn_sqlite__reset(stmt));
13017
13018   if (!have_row)
13019     {
13020       /* Need to set up a new repository row. */
13021       SVN_ERR(create_repos_id(&repos_id, repos_root_url, repos_uuid,
13022                               wcroot->sdb, scratch_pool));
13023     }
13024
13025   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13026                                     STMT_INSERT_EXTERNAL));
13027
13028   /* wc_id, local_relpath, parent_relpath, presence, kind, def_local_relpath,
13029    * repos_id, def_repos_relpath, def_operational_revision, def_revision */
13030   SVN_ERR(svn_sqlite__bindf(stmt, "issstsis",
13031                             wcroot->wc_id,
13032                             svn_dirent_skip_ancestor(wcroot->abspath,
13033                                                      local_abspath),
13034                             svn_dirent_skip_ancestor(wcroot->abspath,
13035                                                      parent_abspath),
13036                             "normal",
13037                             kind_map, kind,
13038                             def_local_relpath,
13039                             repos_id,
13040                             repos_relpath));
13041
13042   if (SVN_IS_VALID_REVNUM(def_peg_revision))
13043     SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, def_peg_revision));
13044
13045   if (SVN_IS_VALID_REVNUM(def_revision))
13046     SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, def_revision));
13047
13048   SVN_ERR(svn_sqlite__insert(NULL, stmt));
13049
13050   return SVN_NO_ERROR;
13051 }
13052
13053 svn_error_t *
13054 svn_wc__db_upgrade_get_repos_id(apr_int64_t *repos_id,
13055                                 svn_sqlite__db_t *sdb,
13056                                 const char *repos_root_url,
13057                                 apr_pool_t *scratch_pool)
13058 {
13059   svn_sqlite__stmt_t *stmt;
13060   svn_boolean_t have_row;
13061
13062   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_SELECT_REPOSITORY));
13063   SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
13064   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13065
13066   if (!have_row)
13067     return svn_error_createf(SVN_ERR_WC_DB_ERROR, svn_sqlite__reset(stmt),
13068                              _("Repository '%s' not found in the database"),
13069                              repos_root_url);
13070
13071   *repos_id = svn_sqlite__column_int64(stmt, 0);
13072   return svn_error_trace(svn_sqlite__reset(stmt));
13073 }
13074
13075
13076 svn_error_t *
13077 svn_wc__db_wq_add(svn_wc__db_t *db,
13078                   const char *wri_abspath,
13079                   const svn_skel_t *work_item,
13080                   apr_pool_t *scratch_pool)
13081 {
13082   svn_wc__db_wcroot_t *wcroot;
13083   const char *local_relpath;
13084
13085   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13086
13087   /* Quick exit, if there are no work items to queue up.  */
13088   if (work_item == NULL)
13089     return SVN_NO_ERROR;
13090
13091   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13092                               wri_abspath, scratch_pool, scratch_pool));
13093   VERIFY_USABLE_WCROOT(wcroot);
13094
13095   /* Add the work item(s) to the WORK_QUEUE.  */
13096   return svn_error_trace(add_work_items(wcroot->sdb, work_item,
13097                                         scratch_pool));
13098 }
13099
13100 /* The body of svn_wc__db_wq_fetch_next().
13101  */
13102 static svn_error_t *
13103 wq_fetch_next(apr_uint64_t *id,
13104               svn_skel_t **work_item,
13105               svn_wc__db_wcroot_t *wcroot,
13106               const char *local_relpath,
13107               apr_uint64_t completed_id,
13108               apr_pool_t *result_pool,
13109               apr_pool_t *scratch_pool)
13110 {
13111   svn_sqlite__stmt_t *stmt;
13112   svn_boolean_t have_row;
13113
13114   if (completed_id != 0)
13115     {
13116       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13117                                         STMT_DELETE_WORK_ITEM));
13118       SVN_ERR(svn_sqlite__bind_int64(stmt, 1, completed_id));
13119
13120       SVN_ERR(svn_sqlite__step_done(stmt));
13121     }
13122
13123   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13124                                     STMT_SELECT_WORK_ITEM));
13125   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13126
13127   if (!have_row)
13128     {
13129       *id = 0;
13130       *work_item = NULL;
13131     }
13132   else
13133     {
13134       apr_size_t len;
13135       const void *val;
13136
13137       *id = svn_sqlite__column_int64(stmt, 0);
13138
13139       val = svn_sqlite__column_blob(stmt, 1, &len, result_pool);
13140
13141       *work_item = svn_skel__parse(val, len, result_pool);
13142     }
13143
13144   return svn_error_trace(svn_sqlite__reset(stmt));
13145 }
13146
13147 svn_error_t *
13148 svn_wc__db_wq_fetch_next(apr_uint64_t *id,
13149                          svn_skel_t **work_item,
13150                          svn_wc__db_t *db,
13151                          const char *wri_abspath,
13152                          apr_uint64_t completed_id,
13153                          apr_pool_t *result_pool,
13154                          apr_pool_t *scratch_pool)
13155 {
13156   svn_wc__db_wcroot_t *wcroot;
13157   const char *local_relpath;
13158
13159   SVN_ERR_ASSERT(id != NULL);
13160   SVN_ERR_ASSERT(work_item != NULL);
13161   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13162
13163   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13164                               wri_abspath, scratch_pool, scratch_pool));
13165   VERIFY_USABLE_WCROOT(wcroot);
13166
13167   SVN_WC__DB_WITH_TXN(
13168     wq_fetch_next(id, work_item,
13169                   wcroot, local_relpath, completed_id,
13170                   result_pool, scratch_pool),
13171     wcroot);
13172
13173   return SVN_NO_ERROR;
13174 }
13175
13176 /* Records timestamp and date for one or more files in wcroot */
13177 static svn_error_t *
13178 wq_record(svn_wc__db_wcroot_t *wcroot,
13179           apr_hash_t *record_map,
13180           apr_pool_t *scratch_pool)
13181 {
13182   apr_hash_index_t *hi;
13183   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
13184
13185   for (hi = apr_hash_first(scratch_pool, record_map); hi;
13186        hi = apr_hash_next(hi))
13187     {
13188       const char *local_abspath = svn__apr_hash_index_key(hi);
13189       const svn_io_dirent2_t *dirent = svn__apr_hash_index_val(hi);
13190       const char *local_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
13191                                                            local_abspath);
13192
13193       svn_pool_clear(iterpool);
13194
13195       if (! local_relpath)
13196         continue;
13197
13198       SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
13199                                  dirent->filesize, dirent->mtime,
13200                                  iterpool));
13201     }
13202
13203   svn_pool_destroy(iterpool);
13204   return SVN_NO_ERROR;
13205 }
13206
13207 svn_error_t *
13208 svn_wc__db_wq_record_and_fetch_next(apr_uint64_t *id,
13209                                     svn_skel_t **work_item,
13210                                     svn_wc__db_t *db,
13211                                     const char *wri_abspath,
13212                                     apr_uint64_t completed_id,
13213                                     apr_hash_t *record_map,
13214                                     apr_pool_t *result_pool,
13215                                     apr_pool_t *scratch_pool)
13216 {
13217   svn_wc__db_wcroot_t *wcroot;
13218   const char *local_relpath;
13219
13220   SVN_ERR_ASSERT(id != NULL);
13221   SVN_ERR_ASSERT(work_item != NULL);
13222   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13223
13224   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13225                               wri_abspath, scratch_pool, scratch_pool));
13226   VERIFY_USABLE_WCROOT(wcroot);
13227
13228   SVN_WC__DB_WITH_TXN(
13229     svn_error_compose_create(
13230             wq_fetch_next(id, work_item,
13231                           wcroot, local_relpath, completed_id,
13232                           result_pool, scratch_pool),
13233             wq_record(wcroot, record_map, scratch_pool)),
13234     wcroot);
13235
13236   return SVN_NO_ERROR;
13237 }
13238
13239
13240
13241 /* ### temporary API. remove before release.  */
13242 svn_error_t *
13243 svn_wc__db_temp_get_format(int *format,
13244                            svn_wc__db_t *db,
13245                            const char *local_dir_abspath,
13246                            apr_pool_t *scratch_pool)
13247 {
13248   svn_wc__db_wcroot_t *wcroot;
13249   const char *local_relpath;
13250   svn_error_t *err;
13251
13252   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13253   /* ### assert that we were passed a directory?  */
13254
13255   err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13256                                 local_dir_abspath, scratch_pool, scratch_pool);
13257
13258   /* If we hit an error examining this directory, then declare this
13259      directory to not be a working copy.  */
13260   if (err)
13261     {
13262       if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
13263         return svn_error_trace(err);
13264       svn_error_clear(err);
13265
13266       /* Remap the returned error.  */
13267       *format = 0;
13268       return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
13269                                _("'%s' is not a working copy"),
13270                                svn_dirent_local_style(local_dir_abspath,
13271                                                       scratch_pool));
13272     }
13273
13274   SVN_ERR_ASSERT(wcroot != NULL);
13275   SVN_ERR_ASSERT(wcroot->format >= 1);
13276
13277   *format = wcroot->format;
13278
13279   return SVN_NO_ERROR;
13280 }
13281
13282 /* ### temporary API. remove before release.  */
13283 svn_wc_adm_access_t *
13284 svn_wc__db_temp_get_access(svn_wc__db_t *db,
13285                            const char *local_dir_abspath,
13286                            apr_pool_t *scratch_pool)
13287 {
13288   const char *local_relpath;
13289   svn_wc__db_wcroot_t *wcroot;
13290   svn_error_t *err;
13291
13292   SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13293
13294   /* ### we really need to assert that we were passed a directory. sometimes
13295      ### adm_retrieve_internal is asked about a file, and then it asks us
13296      ### for an access baton for it. we should definitely return NULL, but
13297      ### ideally: the caller would never ask us about a non-directory.  */
13298
13299   err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13300                             db, local_dir_abspath, scratch_pool, scratch_pool);
13301   if (err)
13302     {
13303       svn_error_clear(err);
13304       return NULL;
13305     }
13306
13307   if (!wcroot)
13308     return NULL;
13309
13310   return svn_hash_gets(wcroot->access_cache, local_dir_abspath);
13311 }
13312
13313
13314 /* ### temporary API. remove before release.  */
13315 void
13316 svn_wc__db_temp_set_access(svn_wc__db_t *db,
13317                            const char *local_dir_abspath,
13318                            svn_wc_adm_access_t *adm_access,
13319                            apr_pool_t *scratch_pool)
13320 {
13321   const char *local_relpath;
13322   svn_wc__db_wcroot_t *wcroot;
13323   svn_error_t *err;
13324
13325   SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13326   /* ### assert that we were passed a directory?  */
13327
13328   err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13329                             db, local_dir_abspath, scratch_pool, scratch_pool);
13330   if (err)
13331     {
13332       /* We don't even have a wcroot, so just bail. */
13333       svn_error_clear(err);
13334       return;
13335     }
13336
13337   /* Better not override something already there.  */
13338   SVN_ERR_ASSERT_NO_RETURN(
13339     svn_hash_gets(wcroot->access_cache, local_dir_abspath) == NULL
13340   );
13341   svn_hash_sets(wcroot->access_cache, local_dir_abspath, adm_access);
13342 }
13343
13344
13345 /* ### temporary API. remove before release.  */
13346 svn_error_t *
13347 svn_wc__db_temp_close_access(svn_wc__db_t *db,
13348                              const char *local_dir_abspath,
13349                              svn_wc_adm_access_t *adm_access,
13350                              apr_pool_t *scratch_pool)
13351 {
13352   const char *local_relpath;
13353   svn_wc__db_wcroot_t *wcroot;
13354
13355   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13356   /* ### assert that we were passed a directory?  */
13357
13358   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13359                               local_dir_abspath, scratch_pool, scratch_pool));
13360   svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13361
13362   return SVN_NO_ERROR;
13363 }
13364
13365
13366 /* ### temporary API. remove before release.  */
13367 void
13368 svn_wc__db_temp_clear_access(svn_wc__db_t *db,
13369                              const char *local_dir_abspath,
13370                              apr_pool_t *scratch_pool)
13371 {
13372   const char *local_relpath;
13373   svn_wc__db_wcroot_t *wcroot;
13374   svn_error_t *err;
13375
13376   SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13377   /* ### assert that we were passed a directory?  */
13378
13379   err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13380                             db, local_dir_abspath, scratch_pool, scratch_pool);
13381   if (err)
13382     {
13383       svn_error_clear(err);
13384       return;
13385     }
13386
13387   svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13388 }
13389
13390
13391 apr_hash_t *
13392 svn_wc__db_temp_get_all_access(svn_wc__db_t *db,
13393                                apr_pool_t *result_pool)
13394 {
13395   apr_hash_t *result = apr_hash_make(result_pool);
13396   apr_hash_index_t *hi;
13397
13398   for (hi = apr_hash_first(result_pool, db->dir_data);
13399        hi;
13400        hi = apr_hash_next(hi))
13401     {
13402       const svn_wc__db_wcroot_t *wcroot = svn__apr_hash_index_val(hi);
13403
13404       /* This is highly redundant, 'cause the same WCROOT will appear many
13405          times in dir_data. */
13406       result = apr_hash_overlay(result_pool, result, wcroot->access_cache);
13407     }
13408
13409   return result;
13410 }
13411
13412
13413 svn_error_t *
13414 svn_wc__db_temp_borrow_sdb(svn_sqlite__db_t **sdb,
13415                            svn_wc__db_t *db,
13416                            const char *local_dir_abspath,
13417                            apr_pool_t *scratch_pool)
13418 {
13419   svn_wc__db_wcroot_t *wcroot;
13420   const char *local_relpath;
13421
13422   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13423
13424   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13425                             local_dir_abspath, scratch_pool, scratch_pool));
13426   VERIFY_USABLE_WCROOT(wcroot);
13427
13428   *sdb = wcroot->sdb;
13429
13430   return SVN_NO_ERROR;
13431 }
13432
13433
13434 svn_error_t *
13435 svn_wc__db_read_conflict_victims(const apr_array_header_t **victims,
13436                                  svn_wc__db_t *db,
13437                                  const char *local_abspath,
13438                                  apr_pool_t *result_pool,
13439                                  apr_pool_t *scratch_pool)
13440 {
13441   svn_wc__db_wcroot_t *wcroot;
13442   const char *local_relpath;
13443   svn_sqlite__stmt_t *stmt;
13444   svn_boolean_t have_row;
13445   apr_array_header_t *new_victims;
13446
13447   /* The parent should be a working copy directory. */
13448   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13449                               local_abspath, scratch_pool, scratch_pool));
13450   VERIFY_USABLE_WCROOT(wcroot);
13451
13452   /* ### This will be much easier once we have all conflicts in one
13453          field of actual*/
13454
13455   /* Look for text, tree and property conflicts in ACTUAL */
13456   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13457                                     STMT_SELECT_CONFLICT_VICTIMS));
13458   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13459
13460   new_victims = apr_array_make(result_pool, 0, sizeof(const char *));
13461
13462   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13463   while (have_row)
13464     {
13465       const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
13466
13467       APR_ARRAY_PUSH(new_victims, const char *) =
13468                             svn_relpath_basename(child_relpath, result_pool);
13469
13470       SVN_ERR(svn_sqlite__step(&have_row, stmt));
13471     }
13472
13473   SVN_ERR(svn_sqlite__reset(stmt));
13474
13475   *victims = new_victims;
13476   return SVN_NO_ERROR;
13477 }
13478
13479 /* The body of svn_wc__db_get_conflict_marker_files().
13480  */
13481 static svn_error_t *
13482 get_conflict_marker_files(apr_hash_t **marker_files_p,
13483                           svn_wc__db_wcroot_t *wcroot,
13484                           const char *local_relpath,
13485                           svn_wc__db_t *db,
13486                           apr_pool_t *result_pool,
13487                           apr_pool_t *scratch_pool)
13488 {
13489   svn_sqlite__stmt_t *stmt;
13490   svn_boolean_t have_row;
13491   apr_hash_t *marker_files = apr_hash_make(result_pool);
13492
13493   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13494                                     STMT_SELECT_ACTUAL_NODE));
13495   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13496   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13497
13498   if (have_row && !svn_sqlite__column_is_null(stmt, 2))
13499     {
13500       apr_size_t len;
13501       const void *data = svn_sqlite__column_blob(stmt, 2, &len, NULL);
13502       svn_skel_t *conflicts;
13503       const apr_array_header_t *markers;
13504       int i;
13505
13506       conflicts = svn_skel__parse(data, len, scratch_pool);
13507
13508       /* ### ADD markers to *marker_files */
13509       SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13510                                             conflicts,
13511                                             result_pool, scratch_pool));
13512
13513       for (i = 0; markers && (i < markers->nelts); i++)
13514         {
13515           const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13516
13517           svn_hash_sets(marker_files, marker_abspath, "");
13518         }
13519     }
13520   SVN_ERR(svn_sqlite__reset(stmt));
13521
13522   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13523                                     STMT_SELECT_CONFLICT_VICTIMS));
13524   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13525   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13526
13527   while (have_row)
13528     {
13529       apr_size_t len;
13530       const void *data = svn_sqlite__column_blob(stmt, 1, &len, NULL);
13531
13532       const apr_array_header_t *markers;
13533       int i;
13534
13535       if (data)
13536         {
13537           svn_skel_t *conflicts;
13538           conflicts = svn_skel__parse(data, len, scratch_pool);
13539
13540           SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13541                                                 conflicts,
13542                                                 result_pool, scratch_pool));
13543
13544           for (i = 0; markers && (i < markers->nelts); i++)
13545             {
13546               const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13547
13548               svn_hash_sets(marker_files, marker_abspath, "");
13549             }
13550         }
13551
13552       SVN_ERR(svn_sqlite__step(&have_row, stmt));
13553     }
13554
13555   if (apr_hash_count(marker_files))
13556     *marker_files_p = marker_files;
13557   else
13558     *marker_files_p = NULL;
13559
13560   return svn_error_trace(svn_sqlite__reset(stmt));
13561 }
13562
13563 svn_error_t *
13564 svn_wc__db_get_conflict_marker_files(apr_hash_t **marker_files,
13565                                      svn_wc__db_t *db,
13566                                      const char *local_abspath,
13567                                      apr_pool_t *result_pool,
13568                                      apr_pool_t *scratch_pool)
13569 {
13570   svn_wc__db_wcroot_t *wcroot;
13571   const char *local_relpath;
13572
13573   /* The parent should be a working copy directory. */
13574   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13575                               local_abspath, scratch_pool, scratch_pool));
13576   VERIFY_USABLE_WCROOT(wcroot);
13577
13578   SVN_WC__DB_WITH_TXN(
13579     get_conflict_marker_files(marker_files, wcroot, local_relpath, db,
13580                               result_pool, scratch_pool),
13581     wcroot);
13582
13583   return SVN_NO_ERROR;
13584 }
13585
13586
13587 svn_error_t *
13588 svn_wc__db_read_conflict(svn_skel_t **conflict,
13589                          svn_wc__db_t *db,
13590                          const char *local_abspath,
13591                          apr_pool_t *result_pool,
13592                          apr_pool_t *scratch_pool)
13593 {
13594   svn_wc__db_wcroot_t *wcroot;
13595   const char *local_relpath;
13596
13597   /* The parent should be a working copy directory. */
13598   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13599                               local_abspath, scratch_pool, scratch_pool));
13600   VERIFY_USABLE_WCROOT(wcroot);
13601
13602   return svn_error_trace(svn_wc__db_read_conflict_internal(conflict, wcroot,
13603                                                            local_relpath,
13604                                                            result_pool,
13605                                                            scratch_pool));
13606 }
13607
13608 svn_error_t *
13609 svn_wc__db_read_conflict_internal(svn_skel_t **conflict,
13610                                   svn_wc__db_wcroot_t *wcroot,
13611                                   const char *local_relpath,
13612                                   apr_pool_t *result_pool,
13613                                   apr_pool_t *scratch_pool)
13614 {
13615   svn_sqlite__stmt_t *stmt;
13616   svn_boolean_t have_row;
13617
13618   /* Check if we have a conflict in ACTUAL */
13619   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13620                                     STMT_SELECT_ACTUAL_NODE));
13621   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13622
13623   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13624
13625   if (! have_row)
13626     {
13627       /* Do this while stmt is still open to avoid closing the sqlite
13628          transaction and then reopening. */
13629       svn_sqlite__stmt_t *stmt_node;
13630       svn_error_t *err;
13631
13632       err = svn_sqlite__get_statement(&stmt_node, wcroot->sdb,
13633                                       STMT_SELECT_NODE_INFO);
13634
13635       if (err)
13636         stmt_node = NULL;
13637       else
13638         err = svn_sqlite__bindf(stmt_node, "is", wcroot->wc_id,
13639                                 local_relpath);
13640
13641       if (!err)
13642         err = svn_sqlite__step(&have_row, stmt_node);
13643
13644       if (stmt_node)
13645         err = svn_error_compose_create(err,
13646                                        svn_sqlite__reset(stmt_node));
13647
13648       SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
13649
13650       if (have_row)
13651         {
13652           *conflict = NULL;
13653           return SVN_NO_ERROR;
13654         }
13655
13656       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13657                                _("The node '%s' was not found."),
13658                                    path_for_error_message(wcroot,
13659                                                           local_relpath,
13660                                                           scratch_pool));
13661     }
13662
13663   {
13664     apr_size_t cfl_len;
13665     const void *cfl_data;
13666
13667     /* svn_skel__parse doesn't copy data, so store in result_pool */
13668     cfl_data = svn_sqlite__column_blob(stmt, 2, &cfl_len, result_pool);
13669
13670     if (cfl_data)
13671       *conflict = svn_skel__parse(cfl_data, cfl_len, result_pool);
13672     else
13673       *conflict = NULL;
13674
13675     return svn_error_trace(svn_sqlite__reset(stmt));
13676   }
13677 }
13678
13679
13680 svn_error_t *
13681 svn_wc__db_read_kind(svn_node_kind_t *kind,
13682                      svn_wc__db_t *db,
13683                      const char *local_abspath,
13684                      svn_boolean_t allow_missing,
13685                      svn_boolean_t show_deleted,
13686                      svn_boolean_t show_hidden,
13687                      apr_pool_t *scratch_pool)
13688 {
13689   svn_wc__db_wcroot_t *wcroot;
13690   const char *local_relpath;
13691   svn_sqlite__stmt_t *stmt_info;
13692   svn_boolean_t have_info;
13693
13694   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13695
13696   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13697                               local_abspath, scratch_pool, scratch_pool));
13698   VERIFY_USABLE_WCROOT(wcroot);
13699
13700   SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
13701                                     STMT_SELECT_NODE_INFO));
13702   SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
13703   SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
13704
13705   if (!have_info)
13706     {
13707       if (allow_missing)
13708         {
13709           *kind = svn_node_unknown;
13710           SVN_ERR(svn_sqlite__reset(stmt_info));
13711           return SVN_NO_ERROR;
13712         }
13713       else
13714         {
13715           SVN_ERR(svn_sqlite__reset(stmt_info));
13716           return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13717                                    _("The node '%s' was not found."),
13718                                    path_for_error_message(wcroot,
13719                                                           local_relpath,
13720                                                           scratch_pool));
13721         }
13722     }
13723
13724   if (!(show_deleted && show_hidden))
13725     {
13726       int op_depth = svn_sqlite__column_int(stmt_info, 0);
13727       svn_boolean_t report_none = FALSE;
13728       svn_wc__db_status_t status = svn_sqlite__column_token(stmt_info, 3,
13729                                                             presence_map);
13730
13731       if (op_depth > 0)
13732         SVN_ERR(convert_to_working_status(&status, status));
13733
13734       switch (status)
13735         {
13736           case svn_wc__db_status_not_present:
13737             if (! (show_hidden && show_deleted))
13738               report_none = TRUE;
13739             break;
13740           case svn_wc__db_status_excluded:
13741           case svn_wc__db_status_server_excluded:
13742             if (! show_hidden)
13743               report_none = TRUE;
13744             break;
13745           case svn_wc__db_status_deleted:
13746             if (! show_deleted)
13747               report_none = TRUE;
13748             break;
13749           default:
13750             break;
13751         }
13752
13753       if (report_none)
13754         {
13755           *kind = svn_node_none;
13756           return svn_error_trace(svn_sqlite__reset(stmt_info));
13757         }
13758     }
13759
13760   *kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
13761
13762   return svn_error_trace(svn_sqlite__reset(stmt_info));
13763 }
13764
13765
13766 svn_error_t *
13767 svn_wc__db_node_hidden(svn_boolean_t *hidden,
13768                        svn_wc__db_t *db,
13769                        const char *local_abspath,
13770                        apr_pool_t *scratch_pool)
13771 {
13772   svn_wc__db_wcroot_t *wcroot;
13773   const char *local_relpath;
13774   svn_wc__db_status_t status;
13775
13776   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13777
13778   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13779                               local_abspath, scratch_pool, scratch_pool));
13780   VERIFY_USABLE_WCROOT(wcroot);
13781
13782   SVN_ERR(read_info(&status, NULL, NULL, NULL, NULL, NULL,
13783                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13784                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13785                     NULL, NULL, NULL,
13786                     wcroot, local_relpath,
13787                     scratch_pool, scratch_pool));
13788
13789   *hidden = (status == svn_wc__db_status_server_excluded
13790              || status == svn_wc__db_status_not_present
13791              || status == svn_wc__db_status_excluded);
13792
13793   return SVN_NO_ERROR;
13794 }
13795
13796
13797 svn_error_t *
13798 svn_wc__db_is_wcroot(svn_boolean_t *is_wcroot,
13799                      svn_wc__db_t *db,
13800                      const char *local_abspath,
13801                      apr_pool_t *scratch_pool)
13802 {
13803   svn_wc__db_wcroot_t *wcroot;
13804   const char *local_relpath;
13805
13806   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13807
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   if (*local_relpath != '\0')
13813     {
13814       *is_wcroot = FALSE; /* Node is a file, or has a parent directory within
13815                            the same wcroot */
13816       return SVN_NO_ERROR;
13817     }
13818
13819    *is_wcroot = TRUE;
13820
13821    return SVN_NO_ERROR;
13822 }
13823
13824 /* Find a node's kind and whether it is switched, putting the outputs in
13825  * *IS_SWITCHED and *KIND. Either of the outputs may be NULL if not wanted.
13826  */
13827 static svn_error_t *
13828 db_is_switched(svn_boolean_t *is_switched,
13829                svn_node_kind_t *kind,
13830                svn_wc__db_wcroot_t *wcroot,
13831                const char *local_relpath,
13832                apr_pool_t *scratch_pool)
13833 {
13834   svn_wc__db_status_t status;
13835   apr_int64_t repos_id;
13836   const char *repos_relpath;
13837   const char *name;
13838   const char *parent_local_relpath;
13839   apr_int64_t parent_repos_id;
13840   const char *parent_repos_relpath;
13841
13842   SVN_ERR_ASSERT(*local_relpath != '\0'); /* Handled in wrapper */
13843
13844   SVN_ERR(read_info(&status, kind, NULL, &repos_relpath, &repos_id, NULL,
13845                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13846                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
13847                     wcroot, local_relpath, scratch_pool, scratch_pool));
13848
13849   if (status == svn_wc__db_status_server_excluded
13850       || status == svn_wc__db_status_excluded
13851       || status == svn_wc__db_status_not_present)
13852     {
13853       return svn_error_createf(
13854                     SVN_ERR_WC_PATH_NOT_FOUND, NULL,
13855                     _("The node '%s' was not found."),
13856                     path_for_error_message(wcroot, local_relpath,
13857                                            scratch_pool));
13858     }
13859   else if (! repos_relpath)
13860     {
13861       /* Node is shadowed; easy out */
13862       if (is_switched)
13863         *is_switched = FALSE;
13864
13865       return SVN_NO_ERROR;
13866     }
13867
13868   if (! is_switched)
13869     return SVN_NO_ERROR;
13870
13871   svn_relpath_split(&parent_local_relpath, &name, local_relpath, scratch_pool);
13872
13873   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
13874                                             &parent_repos_relpath,
13875                                             &parent_repos_id, NULL, NULL, NULL,
13876                                             NULL, NULL, NULL, NULL, NULL,
13877                                             NULL, NULL,
13878                                             wcroot, parent_local_relpath,
13879                                             scratch_pool, scratch_pool));
13880
13881   if (repos_id != parent_repos_id)
13882     *is_switched = TRUE;
13883   else
13884     {
13885       const char *expected_relpath;
13886
13887       expected_relpath = svn_relpath_join(parent_repos_relpath, name,
13888                                           scratch_pool);
13889
13890       *is_switched = (strcmp(expected_relpath, repos_relpath) != 0);
13891     }
13892
13893   return SVN_NO_ERROR;
13894 }
13895
13896 svn_error_t *
13897 svn_wc__db_is_switched(svn_boolean_t *is_wcroot,
13898                        svn_boolean_t *is_switched,
13899                        svn_node_kind_t *kind,
13900                        svn_wc__db_t *db,
13901                        const char *local_abspath,
13902                        apr_pool_t *scratch_pool)
13903 {
13904   svn_wc__db_wcroot_t *wcroot;
13905   const char *local_relpath;
13906
13907   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13908
13909   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13910                               local_abspath, scratch_pool, scratch_pool));
13911   VERIFY_USABLE_WCROOT(wcroot);
13912
13913   if (is_switched)
13914     *is_switched = FALSE;
13915
13916   if (*local_relpath == '\0')
13917     {
13918       /* Easy out */
13919       if (is_wcroot)
13920         *is_wcroot = TRUE;
13921
13922       if (kind)
13923         *kind = svn_node_dir;
13924       return SVN_NO_ERROR;
13925     }
13926
13927   if (is_wcroot)
13928     *is_wcroot = FALSE;
13929
13930   if (! is_switched && ! kind)
13931     return SVN_NO_ERROR;
13932
13933   SVN_WC__DB_WITH_TXN(
13934     db_is_switched(is_switched, kind, wcroot, local_relpath, scratch_pool),
13935     wcroot);
13936   return SVN_NO_ERROR;
13937 }
13938
13939
13940 svn_error_t *
13941 svn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath,
13942                                svn_wc__db_t *db,
13943                                const char *wri_abspath,
13944                                apr_pool_t *result_pool,
13945                                apr_pool_t *scratch_pool)
13946 {
13947   svn_wc__db_wcroot_t *wcroot;
13948   const char *local_relpath;
13949
13950   SVN_ERR_ASSERT(temp_dir_abspath != NULL);
13951   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13952
13953   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13954                               wri_abspath, scratch_pool, scratch_pool));
13955   VERIFY_USABLE_WCROOT(wcroot);
13956
13957   *temp_dir_abspath = svn_dirent_join_many(result_pool,
13958                                            wcroot->abspath,
13959                                            svn_wc_get_adm_dir(scratch_pool),
13960                                            WCROOT_TEMPDIR_RELPATH,
13961                                            NULL);
13962   return SVN_NO_ERROR;
13963 }
13964
13965
13966 /* Helper for wclock_obtain_cb() to steal an existing lock */
13967 static svn_error_t *
13968 wclock_steal(svn_wc__db_wcroot_t *wcroot,
13969              const char *local_relpath,
13970              apr_pool_t *scratch_pool)
13971 {
13972   svn_sqlite__stmt_t *stmt;
13973
13974   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_WC_LOCK));
13975   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13976
13977   SVN_ERR(svn_sqlite__step_done(stmt));
13978
13979   return SVN_NO_ERROR;
13980 }
13981
13982
13983 /* The body of svn_wc__db_wclock_obtain().
13984  */
13985 static svn_error_t *
13986 wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot,
13987                  const char *local_relpath,
13988                  int levels_to_lock,
13989                  svn_boolean_t steal_lock,
13990                  apr_pool_t *scratch_pool)
13991 {
13992   svn_sqlite__stmt_t *stmt;
13993   svn_error_t *err;
13994   const char *lock_relpath;
13995   int max_depth;
13996   int lock_depth;
13997   svn_boolean_t got_row;
13998
13999   svn_wc__db_wclock_t lock;
14000
14001   /* Upgrade locks the root before the node exists.  Apart from that
14002      the root node always exists so we will just skip the check.
14003
14004      ### Perhaps the lock for upgrade should be created when the db is
14005          created?  1.6 used to lock .svn on creation. */
14006   if (local_relpath[0])
14007     {
14008       svn_boolean_t exists;
14009
14010       SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
14011       if (!exists)
14012         return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14013                                  _("The node '%s' was not found."),
14014                                  path_for_error_message(wcroot,
14015                                                         local_relpath,
14016                                                         scratch_pool));
14017     }
14018
14019   /* Check if there are nodes locked below the new lock root */
14020   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_FIND_WC_LOCK));
14021   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14022
14023   lock_depth = relpath_depth(local_relpath);
14024   max_depth = lock_depth + levels_to_lock;
14025
14026   SVN_ERR(svn_sqlite__step(&got_row, stmt));
14027
14028   while (got_row)
14029     {
14030       svn_boolean_t own_lock;
14031
14032       lock_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
14033
14034       /* If we are not locking with depth infinity, check if this lock
14035          voids our lock request */
14036       if (levels_to_lock >= 0
14037           && relpath_depth(lock_relpath) > max_depth)
14038         {
14039           SVN_ERR(svn_sqlite__step(&got_row, stmt));
14040           continue;
14041         }
14042
14043       /* Check if we are the lock owner, because we should be able to
14044          extend our lock. */
14045       err = wclock_owns_lock(&own_lock, wcroot, lock_relpath,
14046                              TRUE, scratch_pool);
14047
14048       if (err)
14049         SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14050
14051       if (!own_lock && !steal_lock)
14052         {
14053           SVN_ERR(svn_sqlite__reset(stmt));
14054           err = svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
14055                                    _("'%s' is already locked."),
14056                                    path_for_error_message(wcroot,
14057                                                           lock_relpath,
14058                                                           scratch_pool));
14059           return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14060                                    _("Working copy '%s' locked."),
14061                                    path_for_error_message(wcroot,
14062                                                           local_relpath,
14063                                                           scratch_pool));
14064         }
14065       else if (!own_lock)
14066         {
14067           err = wclock_steal(wcroot, lock_relpath, scratch_pool);
14068
14069           if (err)
14070             SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14071         }
14072
14073       SVN_ERR(svn_sqlite__step(&got_row, stmt));
14074     }
14075
14076   SVN_ERR(svn_sqlite__reset(stmt));
14077
14078   if (steal_lock)
14079     SVN_ERR(wclock_steal(wcroot, local_relpath, scratch_pool));
14080
14081   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_WC_LOCK));
14082   lock_relpath = local_relpath;
14083
14084   while (TRUE)
14085     {
14086       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, lock_relpath));
14087
14088       SVN_ERR(svn_sqlite__step(&got_row, stmt));
14089
14090       if (got_row)
14091         {
14092           int levels = svn_sqlite__column_int(stmt, 0);
14093           if (levels >= 0)
14094             levels += relpath_depth(lock_relpath);
14095
14096           SVN_ERR(svn_sqlite__reset(stmt));
14097
14098           if (levels == -1 || levels >= lock_depth)
14099             {
14100
14101               err = svn_error_createf(
14102                               SVN_ERR_WC_LOCKED, NULL,
14103                               _("'%s' is already locked."),
14104                               svn_dirent_local_style(
14105                                        svn_dirent_join(wcroot->abspath,
14106                                                        lock_relpath,
14107                                                        scratch_pool),
14108                               scratch_pool));
14109               return svn_error_createf(
14110                               SVN_ERR_WC_LOCKED, err,
14111                               _("Working copy '%s' locked."),
14112                               path_for_error_message(wcroot,
14113                                                      local_relpath,
14114                                                      scratch_pool));
14115             }
14116
14117           break; /* There can't be interesting locks on higher nodes */
14118         }
14119       else
14120         SVN_ERR(svn_sqlite__reset(stmt));
14121
14122       if (!*lock_relpath)
14123         break;
14124
14125       lock_relpath = svn_relpath_dirname(lock_relpath, scratch_pool);
14126     }
14127
14128   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_WC_LOCK));
14129   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14130                             levels_to_lock));
14131   err = svn_sqlite__insert(NULL, stmt);
14132   if (err)
14133     return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14134                              _("Working copy '%s' locked"),
14135                              path_for_error_message(wcroot,
14136                                                     local_relpath,
14137                                                     scratch_pool));
14138
14139   /* And finally store that we obtained the lock */
14140   lock.local_relpath = apr_pstrdup(wcroot->owned_locks->pool, local_relpath);
14141   lock.levels = levels_to_lock;
14142   APR_ARRAY_PUSH(wcroot->owned_locks, svn_wc__db_wclock_t) = lock;
14143
14144   return SVN_NO_ERROR;
14145 }
14146
14147
14148 svn_error_t *
14149 svn_wc__db_wclock_obtain(svn_wc__db_t *db,
14150                          const char *local_abspath,
14151                          int levels_to_lock,
14152                          svn_boolean_t steal_lock,
14153                          apr_pool_t *scratch_pool)
14154 {
14155   svn_wc__db_wcroot_t *wcroot;
14156   const char *local_relpath;
14157
14158   SVN_ERR_ASSERT(levels_to_lock >= -1);
14159   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14160
14161   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14162                                              db, local_abspath,
14163                                              scratch_pool, scratch_pool));
14164   VERIFY_USABLE_WCROOT(wcroot);
14165
14166   if (!steal_lock)
14167     {
14168       int i;
14169       int depth = relpath_depth(local_relpath);
14170
14171       for (i = 0; i < wcroot->owned_locks->nelts; i++)
14172         {
14173           svn_wc__db_wclock_t* lock = &APR_ARRAY_IDX(wcroot->owned_locks,
14174                                                      i, svn_wc__db_wclock_t);
14175
14176           if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14177               && (lock->levels == -1
14178                   || (lock->levels + relpath_depth(lock->local_relpath))
14179                             >= depth))
14180             {
14181               return svn_error_createf(
14182                 SVN_ERR_WC_LOCKED, NULL,
14183                 _("'%s' is already locked via '%s'."),
14184                 svn_dirent_local_style(local_abspath, scratch_pool),
14185                 path_for_error_message(wcroot, lock->local_relpath,
14186                                        scratch_pool));
14187             }
14188         }
14189     }
14190
14191   SVN_WC__DB_WITH_TXN(
14192     wclock_obtain_cb(wcroot, local_relpath, levels_to_lock, steal_lock,
14193                      scratch_pool),
14194     wcroot);
14195   return SVN_NO_ERROR;
14196 }
14197
14198
14199 /* The body of svn_wc__db_wclock_find_root() and svn_wc__db_wclocked(). */
14200 static svn_error_t *
14201 find_wclock(const char **lock_relpath,
14202             svn_wc__db_wcroot_t *wcroot,
14203             const char *dir_relpath,
14204             apr_pool_t *result_pool,
14205             apr_pool_t *scratch_pool)
14206 {
14207   svn_sqlite__stmt_t *stmt;
14208   svn_boolean_t have_row;
14209   int dir_depth = relpath_depth(dir_relpath);
14210   const char *first_relpath;
14211
14212   /* Check for locks on all directories that might be ancestors.
14213      As our new apis only use recursive locks the number of locks stored
14214      in the DB will be very low */
14215   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14216                                     STMT_SELECT_ANCESTOR_WCLOCKS));
14217
14218   /* Get the top level relpath to reduce the worst case number of results
14219      to the number of directories below this node plus two.
14220      (1: the node itself and 2: the wcroot). */
14221   first_relpath = strchr(dir_relpath, '/');
14222
14223   if (first_relpath != NULL)
14224     first_relpath = apr_pstrndup(scratch_pool, dir_relpath,
14225                                  first_relpath - dir_relpath);
14226   else
14227     first_relpath = dir_relpath;
14228
14229   SVN_ERR(svn_sqlite__bindf(stmt, "iss",
14230                             wcroot->wc_id,
14231                             dir_relpath,
14232                             first_relpath));
14233
14234   SVN_ERR(svn_sqlite__step(&have_row, stmt));
14235
14236   while (have_row)
14237     {
14238       const char *relpath = svn_sqlite__column_text(stmt, 0, NULL);
14239
14240       if (svn_relpath_skip_ancestor(relpath, dir_relpath))
14241         {
14242           int locked_levels = svn_sqlite__column_int(stmt, 1);
14243           int row_depth = relpath_depth(relpath);
14244
14245           if (locked_levels == -1
14246               || locked_levels + row_depth >= dir_depth)
14247             {
14248               *lock_relpath = apr_pstrdup(result_pool, relpath);
14249               SVN_ERR(svn_sqlite__reset(stmt));
14250               return SVN_NO_ERROR;
14251             }
14252         }
14253
14254       SVN_ERR(svn_sqlite__step(&have_row, stmt));
14255     }
14256
14257   *lock_relpath = NULL;
14258
14259   return svn_error_trace(svn_sqlite__reset(stmt));
14260 }
14261
14262 static svn_error_t *
14263 is_wclocked(svn_boolean_t *locked,
14264             svn_wc__db_wcroot_t *wcroot,
14265             const char *dir_relpath,
14266             apr_pool_t *scratch_pool)
14267 {
14268   const char *lock_relpath;
14269
14270   SVN_ERR(find_wclock(&lock_relpath, wcroot, dir_relpath,
14271                       scratch_pool, scratch_pool));
14272   *locked = (lock_relpath != NULL);
14273   return SVN_NO_ERROR;
14274 }
14275
14276
14277 svn_error_t*
14278 svn_wc__db_wclock_find_root(const char **lock_abspath,
14279                             svn_wc__db_t *db,
14280                             const char *local_abspath,
14281                             apr_pool_t *result_pool,
14282                             apr_pool_t *scratch_pool)
14283 {
14284   svn_wc__db_wcroot_t *wcroot;
14285   const char *local_relpath;
14286   const char *lock_relpath;
14287
14288   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14289                               local_abspath, scratch_pool, scratch_pool));
14290   VERIFY_USABLE_WCROOT(wcroot);
14291
14292   SVN_WC__DB_WITH_TXN(
14293     find_wclock(&lock_relpath, wcroot, local_relpath,
14294                 scratch_pool, scratch_pool),
14295     wcroot);
14296
14297   if (!lock_relpath)
14298     *lock_abspath = NULL;
14299   else
14300     SVN_ERR(svn_wc__db_from_relpath(lock_abspath, db, wcroot->abspath,
14301                                     lock_relpath, result_pool, scratch_pool));
14302   return SVN_NO_ERROR;
14303 }
14304
14305
14306 svn_error_t *
14307 svn_wc__db_wclocked(svn_boolean_t *locked,
14308                     svn_wc__db_t *db,
14309                     const char *local_abspath,
14310                     apr_pool_t *scratch_pool)
14311 {
14312   svn_wc__db_wcroot_t *wcroot;
14313   const char *local_relpath;
14314
14315   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14316                               local_abspath, scratch_pool, scratch_pool));
14317   VERIFY_USABLE_WCROOT(wcroot);
14318
14319   SVN_WC__DB_WITH_TXN(
14320     is_wclocked(locked, wcroot, local_relpath, scratch_pool),
14321     wcroot);
14322
14323   return SVN_NO_ERROR;
14324 }
14325
14326
14327 svn_error_t *
14328 svn_wc__db_wclock_release(svn_wc__db_t *db,
14329                           const char *local_abspath,
14330                           apr_pool_t *scratch_pool)
14331 {
14332   svn_sqlite__stmt_t *stmt;
14333   svn_wc__db_wcroot_t *wcroot;
14334   const char *local_relpath;
14335   int i;
14336   apr_array_header_t *owned_locks;
14337
14338   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14339                               local_abspath, scratch_pool, scratch_pool));
14340
14341   VERIFY_USABLE_WCROOT(wcroot);
14342
14343   /* First check and remove the owns-lock information as failure in
14344      removing the db record implies that we have to steal the lock later. */
14345   owned_locks = wcroot->owned_locks;
14346   for (i = 0; i < owned_locks->nelts; i++)
14347     {
14348       svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14349                                                  svn_wc__db_wclock_t);
14350
14351       if (strcmp(lock->local_relpath, local_relpath) == 0)
14352         break;
14353     }
14354
14355   if (i >= owned_locks->nelts)
14356     return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
14357                              _("Working copy not locked at '%s'."),
14358                              svn_dirent_local_style(local_abspath,
14359                                                     scratch_pool));
14360
14361   if (i < owned_locks->nelts)
14362     {
14363       owned_locks->nelts--;
14364
14365       /* Move the last item in the array to the deleted place */
14366       if (owned_locks->nelts > 0)
14367         APR_ARRAY_IDX(owned_locks, i, svn_wc__db_wclock_t) =
14368            APR_ARRAY_IDX(owned_locks, owned_locks->nelts, svn_wc__db_wclock_t);
14369     }
14370
14371   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14372                                     STMT_DELETE_WC_LOCK));
14373
14374   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14375
14376   SVN_ERR(svn_sqlite__step_done(stmt));
14377
14378   return SVN_NO_ERROR;
14379 }
14380
14381
14382 /* Like svn_wc__db_wclock_owns_lock() but taking WCROOT+LOCAL_RELPATH instead
14383    of DB+LOCAL_ABSPATH.  */
14384 static svn_error_t *
14385 wclock_owns_lock(svn_boolean_t *own_lock,
14386                  svn_wc__db_wcroot_t *wcroot,
14387                  const char *local_relpath,
14388                  svn_boolean_t exact,
14389                  apr_pool_t *scratch_pool)
14390 {
14391   apr_array_header_t *owned_locks;
14392   int lock_level;
14393   int i;
14394
14395   *own_lock = FALSE;
14396   owned_locks = wcroot->owned_locks;
14397   lock_level = relpath_depth(local_relpath);
14398
14399   if (exact)
14400     {
14401       for (i = 0; i < owned_locks->nelts; i++)
14402         {
14403           svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14404                                                      svn_wc__db_wclock_t);
14405
14406           if (strcmp(lock->local_relpath, local_relpath) == 0)
14407             {
14408               *own_lock = TRUE;
14409               return SVN_NO_ERROR;
14410             }
14411         }
14412     }
14413   else
14414     {
14415       for (i = 0; i < owned_locks->nelts; i++)
14416         {
14417           svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14418                                                      svn_wc__db_wclock_t);
14419
14420           if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14421               && (lock->levels == -1
14422                   || ((relpath_depth(lock->local_relpath) + lock->levels)
14423                       >= lock_level)))
14424             {
14425               *own_lock = TRUE;
14426               return SVN_NO_ERROR;
14427             }
14428         }
14429     }
14430
14431   return SVN_NO_ERROR;
14432 }
14433
14434
14435 svn_error_t *
14436 svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
14437                             svn_wc__db_t *db,
14438                             const char *local_abspath,
14439                             svn_boolean_t exact,
14440                             apr_pool_t *scratch_pool)
14441 {
14442   svn_wc__db_wcroot_t *wcroot;
14443   const char *local_relpath;
14444
14445   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14446                               local_abspath, scratch_pool, scratch_pool));
14447
14448   if (!wcroot)
14449     return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
14450                              _("The node '%s' was not found."),
14451                              svn_dirent_local_style(local_abspath,
14452                                                     scratch_pool));
14453
14454   VERIFY_USABLE_WCROOT(wcroot);
14455
14456   SVN_ERR(wclock_owns_lock(own_lock, wcroot, local_relpath, exact,
14457                            scratch_pool));
14458
14459   return SVN_NO_ERROR;
14460 }
14461
14462 /* The body of svn_wc__db_temp_op_end_directory_update().
14463  */
14464 static svn_error_t *
14465 end_directory_update(svn_wc__db_wcroot_t *wcroot,
14466                      const char *local_relpath,
14467                      apr_pool_t *scratch_pool)
14468 {
14469   svn_sqlite__stmt_t *stmt;
14470   svn_wc__db_status_t base_status;
14471
14472   SVN_ERR(svn_wc__db_base_get_info_internal(&base_status, NULL, NULL, NULL,
14473                                             NULL, NULL, NULL, NULL, NULL,
14474                                             NULL, NULL, NULL, NULL, NULL, NULL,
14475                                             wcroot, local_relpath,
14476                                             scratch_pool, scratch_pool));
14477
14478   if (base_status == svn_wc__db_status_normal)
14479     return SVN_NO_ERROR;
14480
14481   SVN_ERR_ASSERT(base_status == svn_wc__db_status_incomplete);
14482
14483   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14484                                     STMT_UPDATE_NODE_BASE_PRESENCE));
14485   SVN_ERR(svn_sqlite__bindf(stmt, "ist", wcroot->wc_id, local_relpath,
14486                             presence_map, svn_wc__db_status_normal));
14487   SVN_ERR(svn_sqlite__step_done(stmt));
14488
14489   return SVN_NO_ERROR;
14490 }
14491
14492 svn_error_t *
14493 svn_wc__db_temp_op_end_directory_update(svn_wc__db_t *db,
14494                                         const char *local_dir_abspath,
14495                                         apr_pool_t *scratch_pool)
14496 {
14497   svn_wc__db_wcroot_t *wcroot;
14498   const char *local_relpath;
14499
14500   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
14501
14502   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14503                               local_dir_abspath, scratch_pool, scratch_pool));
14504   VERIFY_USABLE_WCROOT(wcroot);
14505
14506   SVN_WC__DB_WITH_TXN(
14507     end_directory_update(wcroot, local_relpath, scratch_pool),
14508     wcroot);
14509
14510   SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_empty,
14511                         scratch_pool));
14512
14513   return SVN_NO_ERROR;
14514 }
14515
14516
14517 /* The body of svn_wc__db_temp_op_start_directory_update().
14518  */
14519 static svn_error_t *
14520 start_directory_update_txn(svn_wc__db_wcroot_t *wcroot,
14521                            const char *local_relpath,
14522                            const char *new_repos_relpath,
14523                            svn_revnum_t new_rev,
14524                            apr_pool_t *scratch_pool)
14525 {
14526   svn_sqlite__stmt_t *stmt;
14527
14528   /* Note: In the majority of calls, the repos_relpath is unchanged. */
14529   /* ### TODO: Maybe check if we can make repos_relpath NULL. */
14530   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14531                     STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH));
14532
14533   SVN_ERR(svn_sqlite__bindf(stmt, "istrs",
14534                             wcroot->wc_id,
14535                             local_relpath,
14536                             presence_map, svn_wc__db_status_incomplete,
14537                             new_rev,
14538                             new_repos_relpath));
14539   SVN_ERR(svn_sqlite__step_done(stmt));
14540
14541   return SVN_NO_ERROR;
14542
14543 }
14544
14545 svn_error_t *
14546 svn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db,
14547                                           const char *local_abspath,
14548                                           const char *new_repos_relpath,
14549                                           svn_revnum_t new_rev,
14550                                           apr_pool_t *scratch_pool)
14551 {
14552   svn_wc__db_wcroot_t *wcroot;
14553   const char *local_relpath;
14554
14555   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14556   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_rev));
14557   SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
14558
14559   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14560                               local_abspath, scratch_pool, scratch_pool));
14561   VERIFY_USABLE_WCROOT(wcroot);
14562
14563   SVN_WC__DB_WITH_TXN(
14564     start_directory_update_txn(wcroot, local_relpath,
14565                                new_repos_relpath, new_rev, scratch_pool),
14566     wcroot);
14567
14568   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
14569
14570   return SVN_NO_ERROR;
14571 }
14572
14573
14574 /* The body of svn_wc__db_temp_op_make_copy().  This is
14575    used by the update editor when deleting a base node tree would be a
14576    tree-conflict because there are changes to subtrees.  This function
14577    inserts a copy of the base node tree below any existing working
14578    subtrees.  Given a tree:
14579
14580              0            1           2            3
14581     /     normal          -
14582     A     normal          -
14583     A/B   normal          -         normal
14584     A/B/C normal          -         base-del       normal
14585     A/F   normal          -         normal
14586     A/F/G normal          -         normal
14587     A/F/H normal          -         base-deleted   normal
14588     A/F/E normal          -         not-present
14589     A/X   normal          -
14590     A/X/Y incomplete      -
14591
14592     This function adds layers to A and some of its descendants in an attempt
14593     to make the working copy look like as if it were a copy of the BASE nodes.
14594
14595              0            1              2            3
14596     /     normal        -
14597     A     normal        norm
14598     A/B   normal        norm        norm
14599     A/B/C normal        norm        base-del       normal
14600     A/F   normal        norm        norm
14601     A/F/G normal        norm        norm
14602     A/F/H normal        norm        not-pres
14603     A/F/E normal        norm        base-del
14604     A/X   normal        norm
14605     A/X/Y incomplete  incomplete
14606  */
14607 static svn_error_t *
14608 make_copy_txn(svn_wc__db_wcroot_t *wcroot,
14609               const char *local_relpath,
14610               int op_depth,
14611               const svn_skel_t *conflicts,
14612               const svn_skel_t *work_items,
14613               apr_pool_t *scratch_pool)
14614 {
14615   svn_sqlite__stmt_t *stmt;
14616   svn_boolean_t have_row;
14617   svn_boolean_t add_working_base_deleted = FALSE;
14618   svn_boolean_t remove_working = FALSE;
14619   const apr_array_header_t *children;
14620   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
14621   int i;
14622
14623   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14624                                     STMT_SELECT_LOWEST_WORKING_NODE));
14625   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
14626   SVN_ERR(svn_sqlite__step(&have_row, stmt));
14627
14628   if (have_row)
14629     {
14630       svn_wc__db_status_t working_status;
14631       int working_op_depth;
14632
14633       working_status = svn_sqlite__column_token(stmt, 1, presence_map);
14634       working_op_depth = svn_sqlite__column_int(stmt, 0);
14635       SVN_ERR(svn_sqlite__reset(stmt));
14636
14637       SVN_ERR_ASSERT(working_status == svn_wc__db_status_normal
14638                      || working_status == svn_wc__db_status_base_deleted
14639                      || working_status == svn_wc__db_status_not_present
14640                      || working_status == svn_wc__db_status_incomplete);
14641
14642       /* Only change nodes in the layers where we are creating the copy.
14643          Deletes in higher layers will just apply to the copy */
14644       if (working_op_depth <= op_depth)
14645         {
14646           add_working_base_deleted = TRUE;
14647
14648           if (working_status == svn_wc__db_status_base_deleted)
14649             remove_working = TRUE;
14650         }
14651     }
14652   else
14653     SVN_ERR(svn_sqlite__reset(stmt));
14654
14655   if (remove_working)
14656     {
14657       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14658                                         STMT_DELETE_LOWEST_WORKING_NODE));
14659       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14660       SVN_ERR(svn_sqlite__step_done(stmt));
14661     }
14662
14663   if (add_working_base_deleted)
14664     {
14665       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14666                                         STMT_INSERT_DELETE_FROM_BASE));
14667       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14668                                 op_depth));
14669       SVN_ERR(svn_sqlite__step_done(stmt));
14670     }
14671   else
14672     {
14673       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14674                                       STMT_INSERT_WORKING_NODE_FROM_BASE_COPY));
14675       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14676                                 op_depth));
14677       SVN_ERR(svn_sqlite__step_done(stmt));
14678     }
14679
14680   /* Get the BASE children, as WORKING children don't need modifications */
14681   SVN_ERR(gather_repo_children(&children, wcroot, local_relpath,
14682                                0, scratch_pool, iterpool));
14683
14684   for (i = 0; i < children->nelts; i++)
14685     {
14686       const char *name = APR_ARRAY_IDX(children, i, const char *);
14687       const char *copy_relpath;
14688
14689       svn_pool_clear(iterpool);
14690
14691       copy_relpath = svn_relpath_join(local_relpath, name, iterpool);
14692
14693       SVN_ERR(make_copy_txn(wcroot, copy_relpath, op_depth, NULL, NULL,
14694                             iterpool));
14695     }
14696
14697   SVN_ERR(flush_entries(wcroot, svn_dirent_join(wcroot->abspath, local_relpath,
14698                                                 iterpool),
14699                                                 svn_depth_empty, iterpool));
14700
14701   if (conflicts)
14702     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
14703                                               conflicts, iterpool));
14704
14705   SVN_ERR(add_work_items(wcroot->sdb, work_items, iterpool));
14706
14707   svn_pool_destroy(iterpool);
14708
14709   return SVN_NO_ERROR;
14710 }
14711
14712
14713 svn_error_t *
14714 svn_wc__db_op_make_copy(svn_wc__db_t *db,
14715                         const char *local_abspath,
14716                         const svn_skel_t *conflicts,
14717                         const svn_skel_t *work_items,
14718                         apr_pool_t *scratch_pool)
14719 {
14720   svn_wc__db_wcroot_t *wcroot;
14721   const char *local_relpath;
14722   svn_sqlite__stmt_t *stmt;
14723   svn_boolean_t have_row;
14724
14725   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14726
14727   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14728                               local_abspath, scratch_pool, scratch_pool));
14729   VERIFY_USABLE_WCROOT(wcroot);
14730
14731   /* The update editor is supposed to call this function when there is
14732      no working node for LOCAL_ABSPATH. */
14733   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14734                                     STMT_SELECT_WORKING_NODE));
14735   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14736   SVN_ERR(svn_sqlite__step(&have_row, stmt));
14737   SVN_ERR(svn_sqlite__reset(stmt));
14738   if (have_row)
14739     return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
14740                              _("Modification of '%s' already exists"),
14741                              path_for_error_message(wcroot,
14742                                                     local_relpath,
14743                                                     scratch_pool));
14744
14745   /* We don't allow copies to contain server-excluded nodes;
14746      the update editor is going to have to bail out. */
14747   SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath, scratch_pool));
14748
14749   SVN_WC__DB_WITH_TXN(
14750     make_copy_txn(wcroot, local_relpath,
14751                   relpath_depth(local_relpath), conflicts, work_items,
14752                   scratch_pool),
14753     wcroot);
14754
14755   return SVN_NO_ERROR;
14756 }
14757
14758 svn_error_t *
14759 svn_wc__db_info_below_working(svn_boolean_t *have_base,
14760                               svn_boolean_t *have_work,
14761                               svn_wc__db_status_t *status,
14762                               svn_wc__db_t *db,
14763                               const char *local_abspath,
14764                               apr_pool_t *scratch_pool)
14765 {
14766   svn_wc__db_wcroot_t *wcroot;
14767   const char *local_relpath;
14768
14769   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14770
14771   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14772                               local_abspath, scratch_pool, scratch_pool));
14773   VERIFY_USABLE_WCROOT(wcroot);
14774   SVN_ERR(info_below_working(have_base, have_work, status,
14775                              wcroot, local_relpath, -1, scratch_pool));
14776
14777   return SVN_NO_ERROR;
14778 }
14779
14780 svn_error_t *
14781 svn_wc__db_get_not_present_descendants(const apr_array_header_t **descendants,
14782                                        svn_wc__db_t *db,
14783                                        const char *local_abspath,
14784                                        apr_pool_t *result_pool,
14785                                        apr_pool_t *scratch_pool)
14786 {
14787   svn_wc__db_wcroot_t *wcroot;
14788   const char *local_relpath;
14789   svn_sqlite__stmt_t *stmt;
14790   svn_boolean_t have_row;
14791
14792   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14793
14794   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14795                               local_abspath, scratch_pool, scratch_pool));
14796   VERIFY_USABLE_WCROOT(wcroot);
14797
14798   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14799                                     STMT_SELECT_NOT_PRESENT_DESCENDANTS));
14800
14801   SVN_ERR(svn_sqlite__bindf(stmt, "isd",
14802                             wcroot->wc_id,
14803                             local_relpath,
14804                             relpath_depth(local_relpath)));
14805
14806   SVN_ERR(svn_sqlite__step(&have_row, stmt));
14807
14808   if (have_row)
14809     {
14810       apr_array_header_t *paths;
14811
14812       paths = apr_array_make(result_pool, 4, sizeof(const char*));
14813       while (have_row)
14814         {
14815           const char *found_relpath = svn_sqlite__column_text(stmt, 0, NULL);
14816
14817           APR_ARRAY_PUSH(paths, const char *)
14818               = apr_pstrdup(result_pool, svn_relpath_skip_ancestor(
14819                                            local_relpath, found_relpath));
14820
14821           SVN_ERR(svn_sqlite__step(&have_row, stmt));
14822         }
14823
14824       *descendants = paths;
14825     }
14826   else
14827     *descendants = apr_array_make(result_pool, 0, sizeof(const char*));
14828
14829   return svn_error_trace(svn_sqlite__reset(stmt));
14830 }
14831
14832
14833 /* Like svn_wc__db_min_max_revisions(),
14834  * but accepts a WCROOT/LOCAL_RELPATH pair. */
14835 static svn_error_t *
14836 get_min_max_revisions(svn_revnum_t *min_revision,
14837                       svn_revnum_t *max_revision,
14838                       svn_wc__db_wcroot_t *wcroot,
14839                       const char *local_relpath,
14840                       svn_boolean_t committed,
14841                       apr_pool_t *scratch_pool)
14842 {
14843   svn_sqlite__stmt_t *stmt;
14844   svn_revnum_t min_rev, max_rev;
14845
14846   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14847                                     STMT_SELECT_MIN_MAX_REVISIONS));
14848   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14849   SVN_ERR(svn_sqlite__step_row(stmt));
14850
14851   if (committed)
14852     {
14853       min_rev = svn_sqlite__column_revnum(stmt, 2);
14854       max_rev = svn_sqlite__column_revnum(stmt, 3);
14855     }
14856   else
14857     {
14858       min_rev = svn_sqlite__column_revnum(stmt, 0);
14859       max_rev = svn_sqlite__column_revnum(stmt, 1);
14860     }
14861
14862   /* The statement returns exactly one row. */
14863   SVN_ERR(svn_sqlite__reset(stmt));
14864
14865   if (min_revision)
14866     *min_revision = min_rev;
14867   if (max_revision)
14868     *max_revision = max_rev;
14869
14870   return SVN_NO_ERROR;
14871 }
14872
14873
14874 svn_error_t *
14875 svn_wc__db_min_max_revisions(svn_revnum_t *min_revision,
14876                              svn_revnum_t *max_revision,
14877                              svn_wc__db_t *db,
14878                              const char *local_abspath,
14879                              svn_boolean_t committed,
14880                              apr_pool_t *scratch_pool)
14881 {
14882   svn_wc__db_wcroot_t *wcroot;
14883   const char *local_relpath;
14884
14885   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14886
14887   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14888                                                 db, local_abspath,
14889                                                 scratch_pool, scratch_pool));
14890   VERIFY_USABLE_WCROOT(wcroot);
14891
14892   return svn_error_trace(get_min_max_revisions(min_revision, max_revision,
14893                                                wcroot, local_relpath,
14894                                                committed, scratch_pool));
14895 }
14896
14897
14898 /* Set *IS_SPARSE_CHECKOUT TRUE if LOCAL_RELPATH or any of the nodes
14899  * within LOCAL_RELPATH is sparse, FALSE otherwise. */
14900 static svn_error_t *
14901 is_sparse_checkout_internal(svn_boolean_t *is_sparse_checkout,
14902                             svn_wc__db_wcroot_t *wcroot,
14903                             const char *local_relpath,
14904                             apr_pool_t *scratch_pool)
14905 {
14906   svn_sqlite__stmt_t *stmt;
14907   svn_boolean_t have_row;
14908
14909   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14910                                     STMT_HAS_SPARSE_NODES));
14911   SVN_ERR(svn_sqlite__bindf(stmt, "is",
14912                             wcroot->wc_id,
14913                             local_relpath));
14914   /* If this query returns a row, the working copy is sparse. */
14915   SVN_ERR(svn_sqlite__step(&have_row, stmt));
14916   *is_sparse_checkout = have_row;
14917   SVN_ERR(svn_sqlite__reset(stmt));
14918
14919   return SVN_NO_ERROR;
14920 }
14921
14922
14923 /* Like svn_wc__db_has_switched_subtrees(),
14924  * but accepts a WCROOT/LOCAL_RELPATH pair. */
14925 static svn_error_t *
14926 has_switched_subtrees(svn_boolean_t *is_switched,
14927                       svn_wc__db_wcroot_t *wcroot,
14928                       const char *local_relpath,
14929                       const char *trail_url,
14930                       apr_pool_t *scratch_pool)
14931 {
14932   svn_sqlite__stmt_t *stmt;
14933   svn_boolean_t have_row;
14934   apr_int64_t repos_id;
14935   const char *repos_relpath;
14936
14937   /* Optional argument handling for caller */
14938   if (!is_switched)
14939     return SVN_NO_ERROR;
14940
14941   *is_switched = FALSE;
14942
14943   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
14944                                             &repos_relpath, &repos_id,
14945                                             NULL, NULL, NULL, NULL, NULL,
14946                                             NULL, NULL, NULL, NULL, NULL,
14947                                             wcroot, local_relpath,
14948                                             scratch_pool, scratch_pool));
14949
14950   /* First do the cheap check where we only need info on the origin itself */
14951   if (trail_url != NULL)
14952     {
14953       const char *repos_root_url;
14954       const char *url;
14955       apr_size_t len1, len2;
14956
14957       /* If the trailing part of the URL of the working copy directory
14958          does not match the given trailing URL then the whole working
14959          copy is switched. */
14960
14961       SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot->sdb,
14962                                           repos_id, scratch_pool));
14963       url = svn_path_url_add_component2(repos_root_url, repos_relpath,
14964                                         scratch_pool);
14965
14966       len1 = strlen(trail_url);
14967       len2 = strlen(url);
14968       if ((len1 > len2) || strcmp(url + len2 - len1, trail_url))
14969         {
14970           *is_switched = TRUE;
14971           return SVN_NO_ERROR;
14972         }
14973     }
14974
14975   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_HAS_SWITCHED));
14976   SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath, repos_relpath));
14977   SVN_ERR(svn_sqlite__step(&have_row, stmt));
14978   if (have_row)
14979     *is_switched = TRUE;
14980   SVN_ERR(svn_sqlite__reset(stmt));
14981
14982   return SVN_NO_ERROR;
14983 }
14984
14985
14986 svn_error_t *
14987 svn_wc__db_has_switched_subtrees(svn_boolean_t *is_switched,
14988                                  svn_wc__db_t *db,
14989                                  const char *local_abspath,
14990                                  const char *trail_url,
14991                                  apr_pool_t *scratch_pool)
14992 {
14993   svn_wc__db_wcroot_t *wcroot;
14994   const char *local_relpath;
14995
14996   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14997
14998   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14999                                                 db, local_abspath,
15000                                                 scratch_pool, scratch_pool));
15001   VERIFY_USABLE_WCROOT(wcroot);
15002
15003   return svn_error_trace(has_switched_subtrees(is_switched, wcroot,
15004                                                local_relpath, trail_url,
15005                                                scratch_pool));
15006 }
15007
15008 svn_error_t *
15009 svn_wc__db_get_excluded_subtrees(apr_hash_t **excluded_subtrees,
15010                                  svn_wc__db_t *db,
15011                                  const char *local_abspath,
15012                                  apr_pool_t *result_pool,
15013                                  apr_pool_t *scratch_pool)
15014 {
15015   svn_wc__db_wcroot_t *wcroot;
15016   const char *local_relpath;
15017   svn_sqlite__stmt_t *stmt;
15018   svn_boolean_t have_row;
15019
15020   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15021   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15022                                                 db, local_abspath,
15023                                                 scratch_pool, scratch_pool));
15024   VERIFY_USABLE_WCROOT(wcroot);
15025
15026   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15027                                     STMT_SELECT_ALL_EXCLUDED_DESCENDANTS));
15028   SVN_ERR(svn_sqlite__bindf(stmt, "is",
15029                             wcroot->wc_id,
15030                             local_relpath));
15031   SVN_ERR(svn_sqlite__step(&have_row, stmt));
15032
15033   if (have_row)
15034     *excluded_subtrees = apr_hash_make(result_pool);
15035   else
15036     *excluded_subtrees = NULL;
15037
15038   while (have_row)
15039     {
15040       const char *abs_path =
15041         svn_dirent_join(wcroot->abspath,
15042                         svn_sqlite__column_text(stmt, 0, NULL),
15043                         result_pool);
15044       svn_hash_sets(*excluded_subtrees, abs_path, abs_path);
15045       SVN_ERR(svn_sqlite__step(&have_row, stmt));
15046     }
15047
15048   SVN_ERR(svn_sqlite__reset(stmt));
15049   return SVN_NO_ERROR;
15050 }
15051
15052 /* Like svn_wc__db_has_local_mods(),
15053  * but accepts a WCROOT/LOCAL_RELPATH pair.
15054  * ### This needs a DB as well as a WCROOT/RELPATH pair... */
15055 static svn_error_t *
15056 has_local_mods(svn_boolean_t *is_modified,
15057                svn_wc__db_wcroot_t *wcroot,
15058                const char *local_relpath,
15059                svn_wc__db_t *db,
15060                svn_cancel_func_t cancel_func,
15061                void *cancel_baton,
15062                apr_pool_t *scratch_pool)
15063 {
15064   svn_sqlite__stmt_t *stmt;
15065
15066   /* Check for additions or deletions. */
15067   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15068                                     STMT_SUBTREE_HAS_TREE_MODIFICATIONS));
15069   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15070   /* If this query returns a row, the working copy is modified. */
15071   SVN_ERR(svn_sqlite__step(is_modified, stmt));
15072   SVN_ERR(svn_sqlite__reset(stmt));
15073
15074   if (cancel_func)
15075     SVN_ERR(cancel_func(cancel_baton));
15076
15077   if (! *is_modified)
15078     {
15079       /* Check for property modifications. */
15080       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15081                                         STMT_SUBTREE_HAS_PROP_MODIFICATIONS));
15082       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15083       /* If this query returns a row, the working copy is modified. */
15084       SVN_ERR(svn_sqlite__step(is_modified, stmt));
15085       SVN_ERR(svn_sqlite__reset(stmt));
15086
15087       if (cancel_func)
15088         SVN_ERR(cancel_func(cancel_baton));
15089     }
15090
15091   if (! *is_modified)
15092     {
15093       apr_pool_t *iterpool = NULL;
15094       svn_boolean_t have_row;
15095
15096       /* Check for text modifications. */
15097       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15098                                         STMT_SELECT_BASE_FILES_RECURSIVE));
15099       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15100       SVN_ERR(svn_sqlite__step(&have_row, stmt));
15101       if (have_row)
15102         iterpool = svn_pool_create(scratch_pool);
15103       while (have_row)
15104         {
15105           const char *node_abspath;
15106           svn_filesize_t recorded_size;
15107           apr_time_t recorded_time;
15108           svn_boolean_t skip_check = FALSE;
15109           svn_error_t *err;
15110
15111           if (cancel_func)
15112             {
15113               err = cancel_func(cancel_baton);
15114               if (err)
15115                 return svn_error_trace(svn_error_compose_create(
15116                                                     err,
15117                                                     svn_sqlite__reset(stmt)));
15118             }
15119
15120           svn_pool_clear(iterpool);
15121
15122           node_abspath = svn_dirent_join(wcroot->abspath,
15123                                          svn_sqlite__column_text(stmt, 0,
15124                                                                  iterpool),
15125                                          iterpool);
15126
15127           recorded_size = get_recorded_size(stmt, 1);
15128           recorded_time = svn_sqlite__column_int64(stmt, 2);
15129
15130           if (recorded_size != SVN_INVALID_FILESIZE
15131               && recorded_time != 0)
15132             {
15133               const svn_io_dirent2_t *dirent;
15134
15135               err = svn_io_stat_dirent2(&dirent, node_abspath, FALSE, TRUE,
15136                                         iterpool, iterpool);
15137               if (err)
15138                 return svn_error_trace(svn_error_compose_create(
15139                                                     err,
15140                                                     svn_sqlite__reset(stmt)));
15141
15142               if (dirent->kind != svn_node_file)
15143                 {
15144                   *is_modified = TRUE; /* Missing or obstruction */
15145                   break;
15146                 }
15147               else if (dirent->filesize == recorded_size
15148                        && dirent->mtime == recorded_time)
15149                 {
15150                   /* The file is not modified */
15151                   skip_check = TRUE;
15152                 }
15153             }
15154
15155           if (! skip_check)
15156             {
15157               err = svn_wc__internal_file_modified_p(is_modified,
15158                                                      db, node_abspath,
15159                                                      FALSE, iterpool);
15160
15161               if (err)
15162                 return svn_error_trace(svn_error_compose_create(
15163                                                     err,
15164                                                     svn_sqlite__reset(stmt)));
15165
15166               if (*is_modified)
15167                 break;
15168             }
15169
15170           SVN_ERR(svn_sqlite__step(&have_row, stmt));
15171         }
15172       if (iterpool)
15173         svn_pool_destroy(iterpool);
15174
15175       SVN_ERR(svn_sqlite__reset(stmt));
15176     }
15177
15178   return SVN_NO_ERROR;
15179 }
15180
15181
15182 svn_error_t *
15183 svn_wc__db_has_local_mods(svn_boolean_t *is_modified,
15184                           svn_wc__db_t *db,
15185                           const char *local_abspath,
15186                           svn_cancel_func_t cancel_func,
15187                           void *cancel_baton,
15188                           apr_pool_t *scratch_pool)
15189 {
15190   svn_wc__db_wcroot_t *wcroot;
15191   const char *local_relpath;
15192
15193   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15194
15195   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15196                                                 db, local_abspath,
15197                                                 scratch_pool, scratch_pool));
15198   VERIFY_USABLE_WCROOT(wcroot);
15199
15200   return svn_error_trace(has_local_mods(is_modified, wcroot, local_relpath,
15201                                         db, cancel_func, cancel_baton,
15202                                         scratch_pool));
15203 }
15204
15205
15206 /* The body of svn_wc__db_revision_status().
15207  */
15208 static svn_error_t *
15209 revision_status_txn(svn_revnum_t *min_revision,
15210                     svn_revnum_t *max_revision,
15211                     svn_boolean_t *is_sparse_checkout,
15212                     svn_boolean_t *is_modified,
15213                     svn_boolean_t *is_switched,
15214                     svn_wc__db_wcroot_t *wcroot,
15215                     const char *local_relpath,
15216                     svn_wc__db_t *db,
15217                     const char *trail_url,
15218                     svn_boolean_t committed,
15219                     svn_cancel_func_t cancel_func,
15220                     void *cancel_baton,
15221                     apr_pool_t *scratch_pool)
15222 {
15223   svn_error_t *err;
15224   svn_boolean_t exists;
15225
15226   SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
15227
15228   if (!exists)
15229     {
15230       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
15231                                _("The node '%s' was not found."),
15232                                path_for_error_message(wcroot, local_relpath,
15233                                                       scratch_pool));
15234     }
15235
15236   /* Determine mixed-revisionness. */
15237   SVN_ERR(get_min_max_revisions(min_revision, max_revision, wcroot,
15238                                 local_relpath, committed, scratch_pool));
15239
15240   if (cancel_func)
15241     SVN_ERR(cancel_func(cancel_baton));
15242
15243   /* Determine sparseness. */
15244   SVN_ERR(is_sparse_checkout_internal(is_sparse_checkout, wcroot,
15245                                       local_relpath, scratch_pool));
15246
15247   if (cancel_func)
15248     SVN_ERR(cancel_func(cancel_baton));
15249
15250   /* Check for switched nodes. */
15251   {
15252     err = has_switched_subtrees(is_switched, wcroot, local_relpath,
15253                                 trail_url, scratch_pool);
15254
15255     if (err)
15256       {
15257         if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
15258           return svn_error_trace(err);
15259
15260         svn_error_clear(err); /* No Base node, but no fatal error */
15261         *is_switched = FALSE;
15262       }
15263   }
15264
15265   if (cancel_func)
15266     SVN_ERR(cancel_func(cancel_baton));
15267
15268   /* Check for local mods. */
15269   SVN_ERR(has_local_mods(is_modified, wcroot, local_relpath, db,
15270                          cancel_func, cancel_baton, scratch_pool));
15271
15272   return SVN_NO_ERROR;
15273 }
15274
15275
15276 svn_error_t *
15277 svn_wc__db_revision_status(svn_revnum_t *min_revision,
15278                            svn_revnum_t *max_revision,
15279                            svn_boolean_t *is_sparse_checkout,
15280                            svn_boolean_t *is_modified,
15281                            svn_boolean_t *is_switched,
15282                            svn_wc__db_t *db,
15283                            const char *local_abspath,
15284                            const char *trail_url,
15285                            svn_boolean_t committed,
15286                            svn_cancel_func_t cancel_func,
15287                            void *cancel_baton,
15288                            apr_pool_t *scratch_pool)
15289 {
15290   svn_wc__db_wcroot_t *wcroot;
15291   const char *local_relpath;
15292
15293   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15294
15295   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15296                                                 db, local_abspath,
15297                                                 scratch_pool, scratch_pool));
15298   VERIFY_USABLE_WCROOT(wcroot);
15299
15300   SVN_WC__DB_WITH_TXN(
15301     revision_status_txn(min_revision, max_revision,
15302                         is_sparse_checkout, is_modified, is_switched,
15303                         wcroot, local_relpath, db,
15304                         trail_url, committed, cancel_func, cancel_baton,
15305                         scratch_pool),
15306     wcroot);
15307   return SVN_NO_ERROR;
15308 }
15309
15310
15311 svn_error_t *
15312 svn_wc__db_base_get_lock_tokens_recursive(apr_hash_t **lock_tokens,
15313                                           svn_wc__db_t *db,
15314                                           const char *local_abspath,
15315                                           apr_pool_t *result_pool,
15316                                           apr_pool_t *scratch_pool)
15317 {
15318   svn_wc__db_wcroot_t *wcroot;
15319   const char *local_relpath;
15320   svn_sqlite__stmt_t *stmt;
15321   svn_boolean_t have_row;
15322   apr_int64_t last_repos_id = INVALID_REPOS_ID;
15323   const char *last_repos_root_url = NULL;
15324
15325   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15326
15327   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15328                                                 db, local_abspath,
15329                                                 scratch_pool, scratch_pool));
15330   VERIFY_USABLE_WCROOT(wcroot);
15331
15332   *lock_tokens = apr_hash_make(result_pool);
15333
15334   /* Fetch all the lock tokens in and under LOCAL_RELPATH. */
15335   SVN_ERR(svn_sqlite__get_statement(
15336               &stmt, wcroot->sdb,
15337               STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE));
15338
15339   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15340   SVN_ERR(svn_sqlite__step(&have_row, stmt));
15341   while (have_row)
15342     {
15343       apr_int64_t child_repos_id = svn_sqlite__column_int64(stmt, 0);
15344       const char *child_relpath = svn_sqlite__column_text(stmt, 1, NULL);
15345       const char *lock_token = svn_sqlite__column_text(stmt, 2, result_pool);
15346
15347       if (child_repos_id != last_repos_id)
15348         {
15349           svn_error_t *err = svn_wc__db_fetch_repos_info(&last_repos_root_url,
15350                                                          NULL, wcroot->sdb,
15351                                                          child_repos_id,
15352                                                          scratch_pool);
15353
15354           if (err)
15355             {
15356               return svn_error_trace(
15357                             svn_error_compose_create(err,
15358                                                      svn_sqlite__reset(stmt)));
15359             }
15360
15361           last_repos_id = child_repos_id;
15362         }
15363
15364       SVN_ERR_ASSERT(last_repos_root_url != NULL);
15365       svn_hash_sets(*lock_tokens,
15366                     svn_path_url_add_component2(last_repos_root_url,
15367                                                 child_relpath, result_pool),
15368                     lock_token);
15369
15370       SVN_ERR(svn_sqlite__step(&have_row, stmt));
15371     }
15372   return svn_sqlite__reset(stmt);
15373 }
15374
15375
15376 /* If EXPRESSION is false, cause the caller to return an SVN_ERR_WC_CORRUPT
15377  * error, showing EXPRESSION and the caller's LOCAL_RELPATH in the message. */
15378 #define VERIFY(expression) \
15379   do { \
15380     if (! (expression)) \
15381       return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, \
15382         _("database inconsistency at local_relpath='%s' verifying " \
15383           "expression '%s'"), local_relpath, #expression); \
15384   } while (0)
15385
15386
15387 /* Verify consistency of the metadata concerning WCROOT.  This is intended
15388  * for use only during testing and debugging, so is not intended to be
15389  * blazingly fast.
15390  *
15391  * This code is a complement to any verification that we can do in SQLite
15392  * triggers.  See, for example, 'wc-checks.sql'.
15393  *
15394  * Some more verification steps we might want to add are:
15395  *
15396  *   * on every ACTUAL row (except root): a NODES row exists at its parent path
15397  *   * the op-depth root must always exist and every intermediate too
15398  */
15399 static svn_error_t *
15400 verify_wcroot(svn_wc__db_wcroot_t *wcroot,
15401               apr_pool_t *scratch_pool)
15402 {
15403   svn_sqlite__stmt_t *stmt;
15404   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15405
15406   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15407                                     STMT_SELECT_ALL_NODES));
15408   SVN_ERR(svn_sqlite__bindf(stmt, "i", wcroot->wc_id));
15409   while (TRUE)
15410     {
15411       svn_boolean_t have_row;
15412       const char *local_relpath, *parent_relpath;
15413       int op_depth;
15414
15415       svn_pool_clear(iterpool);
15416
15417       SVN_ERR(svn_sqlite__step(&have_row, stmt));
15418       if (!have_row)
15419         break;
15420
15421       op_depth = svn_sqlite__column_int(stmt, 0);
15422       local_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
15423       parent_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
15424
15425       /* Verify parent_relpath is the parent path of local_relpath */
15426       VERIFY((parent_relpath == NULL)
15427              ? (local_relpath[0] == '\0')
15428              : (strcmp(svn_relpath_dirname(local_relpath, iterpool),
15429                        parent_relpath) == 0));
15430
15431       /* Verify op_depth <= the tree depth of local_relpath */
15432       VERIFY(op_depth <= relpath_depth(local_relpath));
15433
15434       /* Verify parent_relpath refers to a row that exists */
15435       /* TODO: Verify there is a suitable parent row - e.g. has op_depth <=
15436        * the child's and a suitable presence */
15437       if (parent_relpath && svn_sqlite__column_is_null(stmt, 3))
15438         {
15439           svn_sqlite__stmt_t *stmt2;
15440           svn_boolean_t have_a_parent_row;
15441
15442           SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
15443                                             STMT_SELECT_NODE_INFO));
15444           SVN_ERR(svn_sqlite__bindf(stmt2, "is", wcroot->wc_id,
15445                                     parent_relpath));
15446           SVN_ERR(svn_sqlite__step(&have_a_parent_row, stmt2));
15447           VERIFY(have_a_parent_row);
15448           SVN_ERR(svn_sqlite__reset(stmt2));
15449         }
15450     }
15451   svn_pool_destroy(iterpool);
15452
15453   return svn_error_trace(svn_sqlite__reset(stmt));
15454 }
15455
15456 svn_error_t *
15457 svn_wc__db_verify(svn_wc__db_t *db,
15458                   const char *wri_abspath,
15459                   apr_pool_t *scratch_pool)
15460 {
15461   svn_wc__db_wcroot_t *wcroot;
15462   const char *local_relpath;
15463
15464   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15465                                                 db, wri_abspath,
15466                                                 scratch_pool, scratch_pool));
15467   VERIFY_USABLE_WCROOT(wcroot);
15468
15469   SVN_ERR(verify_wcroot(wcroot, scratch_pool));
15470   return SVN_NO_ERROR;
15471 }
15472
15473 svn_error_t *
15474 svn_wc__db_bump_format(int *result_format,
15475                        svn_boolean_t *bumped_format,
15476                        svn_wc__db_t *db,
15477                        const char *wcroot_abspath,
15478                        apr_pool_t *scratch_pool)
15479 {
15480   svn_sqlite__db_t *sdb;
15481   svn_error_t *err;
15482   int format;
15483
15484   if (bumped_format)
15485     *bumped_format = FALSE;
15486
15487   /* Do not scan upwards for a working copy root here to prevent accidental
15488    * upgrades of any working copies the WCROOT might be nested in.
15489    * Just try to open a DB at the specified path instead. */
15490   err = svn_wc__db_util_open_db(&sdb, wcroot_abspath, SDB_FILE,
15491                                 svn_sqlite__mode_readwrite,
15492                                 TRUE, /* exclusive */
15493                                 NULL, /* my statements */
15494                                 scratch_pool, scratch_pool);
15495   if (err)
15496     {
15497       svn_error_t *err2;
15498       apr_hash_t *entries;
15499
15500       /* Could not open an sdb. Check for an entries file instead. */
15501       err2 = svn_wc__read_entries_old(&entries, wcroot_abspath,
15502                                       scratch_pool, scratch_pool);
15503       if (err2 || apr_hash_count(entries) == 0)
15504         return svn_error_createf(SVN_ERR_WC_INVALID_OP_ON_CWD,
15505                   svn_error_compose_create(err, err2),
15506                   _("Can't upgrade '%s' as it is not a working copy root"),
15507                   svn_dirent_local_style(wcroot_abspath, scratch_pool));
15508
15509       /* An entries file was found. This is a pre-wc-ng working copy
15510        * so suggest an upgrade. */
15511       return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, err,
15512                 _("Working copy '%s' is too old and must be upgraded to "
15513                   "at least format %d, as created by Subversion %s"),
15514                 svn_dirent_local_style(wcroot_abspath, scratch_pool),
15515                 SVN_WC__WC_NG_VERSION,
15516                 svn_wc__version_string_from_format(SVN_WC__WC_NG_VERSION));
15517     }
15518
15519   SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool));
15520   err = svn_wc__upgrade_sdb(result_format, wcroot_abspath,
15521                             sdb, format, scratch_pool);
15522
15523   if (err == SVN_NO_ERROR && bumped_format)
15524     *bumped_format = (*result_format > format);
15525
15526   /* Make sure we return a different error than expected for upgrades from
15527      entries */
15528   if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
15529     err = svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, err,
15530                            _("Working copy upgrade failed"));
15531
15532   err = svn_error_compose_create(err, svn_sqlite__close(sdb));
15533
15534   return svn_error_trace(err);
15535 }
15536
15537 svn_error_t *
15538 svn_wc__db_vacuum(svn_wc__db_t *db,
15539                   const char *local_abspath,
15540                   apr_pool_t *scratch_pool)
15541 {
15542   svn_wc__db_wcroot_t *wcroot;
15543   const char *local_relpath;
15544
15545   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15546                                                 db, local_abspath,
15547                                                 scratch_pool, scratch_pool));
15548   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_VACUUM));
15549
15550   return SVN_NO_ERROR;
15551 }