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