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