]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/subversion/subversion/libsvn_wc/wc_db.c
Update svn-1.9.7 to 1.10.0.
[FreeBSD/FreeBSD.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_private_config.h"
31 #include "svn_types.h"
32 #include "svn_error.h"
33 #include "svn_dirent_uri.h"
34 #include "svn_path.h"
35 #include "svn_hash.h"
36 #include "svn_sorts.h"
37 #include "svn_wc.h"
38 #include "svn_checksum.h"
39 #include "svn_pools.h"
40
41 #include "wc.h"
42 #include "wc_db.h"
43 #include "adm_files.h"
44 #include "wc-queries.h"
45 #include "entries.h"
46 #include "lock.h"
47 #include "conflicts.h"
48 #include "wc_db_private.h"
49 #include "workqueue.h"
50 #include "token-map.h"
51
52 #include "private/svn_sorts_private.h"
53 #include "private/svn_sqlite.h"
54 #include "private/svn_skel.h"
55 #include "private/svn_wc_private.h"
56 #include "private/svn_token.h"
57
58
59 #define NOT_IMPLEMENTED() SVN__NOT_IMPLEMENTED()
60
61
62 /*
63  * Some filename constants.
64  */
65 #define SDB_FILE  "wc.db"
66
67 #define WCROOT_TEMPDIR_RELPATH   "tmp"
68
69
70 /*
71  * PARAMETER ASSERTIONS
72  *
73  * Every (semi-)public entrypoint in this file has a set of assertions on
74  * the parameters passed into the function. Since this is a brand new API,
75  * we want to make sure that everybody calls it properly. The original WC
76  * code had years to catch stray bugs, but we do not have that luxury in
77  * the wc-nb rewrite. Any extra assurances that we can find will be
78  * welcome. The asserts will ensure we have no doubt about the values
79  * passed into the function.
80  *
81  * Some parameters are *not* specifically asserted. Typically, these are
82  * params that will be used immediately, so something like a NULL value
83  * will be obvious.
84  *
85  * ### near 1.7 release, it would be a Good Thing to review the assertions
86  * ### and decide if any can be removed or switched to assert() in order
87  * ### to remove their runtime cost in the production release.
88  *
89  *
90  * DATABASE OPERATIONS
91  *
92  * Each function should leave the database in a consistent state. If it
93  * does *not*, then the implication is some other function needs to be
94  * called to restore consistency. Subtle requirements like that are hard
95  * to maintain over a long period of time, so this API will not allow it.
96  *
97  *
98  * STANDARD VARIABLE NAMES
99  *
100  * db     working copy database (this module)
101  * sdb    SQLite database (not to be confused with 'db')
102  * wc_id  a WCROOT id associated with a node
103  */
104
105 #define INVALID_REPOS_ID ((apr_int64_t) -1)
106 #define UNKNOWN_WC_ID ((apr_int64_t) -1)
107 #define FORMAT_FROM_SDB (-1)
108
109 /* Check if column number I, a property-skel column, contains a non-empty
110    set of properties. The empty set of properties is stored as "()", so we
111    have properties if the size of the column is larger than 2. */
112 #define SQLITE_PROPERTIES_AVAILABLE(stmt, i) \
113                  (svn_sqlite__column_bytes(stmt, i) > 2)
114
115 int
116 svn_wc__db_op_depth_for_upgrade(const char *local_relpath)
117 {
118   return relpath_depth(local_relpath);
119 }
120
121
122 /* Representation of a new base row for the NODES table */
123 typedef struct insert_base_baton_t {
124   /* common to all insertions into BASE */
125   svn_wc__db_status_t status;
126   svn_node_kind_t kind;
127   apr_int64_t repos_id;
128   const char *repos_relpath;
129   svn_revnum_t revision;
130
131   /* Only used when repos_id == INVALID_REPOS_ID */
132   const char *repos_root_url;
133   const char *repos_uuid;
134
135   /* common to all "normal" presence insertions */
136   const apr_hash_t *props;
137   svn_revnum_t changed_rev;
138   apr_time_t changed_date;
139   const char *changed_author;
140   const apr_hash_t *dav_cache;
141
142   /* for inserting directories */
143   const apr_array_header_t *children;
144   svn_depth_t depth;
145
146   /* for inserting files */
147   const svn_checksum_t *checksum;
148
149   /* for inserting symlinks */
150   const char *target;
151
152   svn_boolean_t file_external;
153
154   /* may need to insert/update ACTUAL to record a conflict  */
155   const svn_skel_t *conflict;
156
157   /* may need to insert/update ACTUAL to record new properties */
158   svn_boolean_t update_actual_props;
159   const apr_hash_t *new_actual_props;
160
161   /* A depth-first ordered array of svn_prop_inherited_item_t *
162      structures representing the properties inherited by the base
163      node. */
164   apr_array_header_t *iprops;
165
166   /* maybe we should copy information from a previous record? */
167   svn_boolean_t keep_recorded_info;
168
169   /* insert a base-deleted working node as well as a base node */
170   svn_boolean_t insert_base_deleted;
171
172   /* delete the current working nodes above BASE */
173   svn_boolean_t delete_working;
174
175   /* may have work items to queue in this transaction  */
176   const svn_skel_t *work_items;
177
178 } insert_base_baton_t;
179
180
181 /* Representation of a new working row for the NODES table */
182 typedef struct insert_working_baton_t {
183   /* common to all insertions into WORKING (including NODE_DATA) */
184   svn_wc__db_status_t presence;
185   svn_node_kind_t kind;
186   int op_depth;
187
188   /* common to all "normal" presence insertions */
189   const apr_hash_t *props;
190   svn_revnum_t changed_rev;
191   apr_time_t changed_date;
192   const char *changed_author;
193   apr_int64_t original_repos_id;
194   const char *original_repos_relpath;
195   svn_revnum_t original_revnum;
196   svn_boolean_t moved_here;
197
198   /* for inserting directories */
199   const apr_array_header_t *children;
200   svn_depth_t depth;
201
202   /* for inserting (copied/moved-here) files */
203   const svn_checksum_t *checksum;
204
205   /* for inserting symlinks */
206   const char *target;
207
208   svn_boolean_t update_actual_props;
209   const apr_hash_t *new_actual_props;
210
211   /* may have work items to queue in this transaction  */
212   const svn_skel_t *work_items;
213
214   /* may have conflict to install in this transaction */
215   const svn_skel_t *conflict;
216
217   /* If the value is > 0 and < op_depth, also insert a not-present
218      at op-depth NOT_PRESENT_OP_DEPTH, based on this same information */
219   int not_present_op_depth;
220
221 } insert_working_baton_t;
222
223 /* Representation of a new row for the EXTERNALS table */
224 typedef struct insert_external_baton_t {
225   /* common to all insertions into EXTERNALS */
226   svn_node_kind_t kind;
227   svn_wc__db_status_t presence;
228
229   /* The repository of the external */
230   apr_int64_t repos_id;
231   /* for file and symlink externals */
232   const char *repos_relpath;
233   svn_revnum_t revision;
234
235   /* Only used when repos_id == INVALID_REPOS_ID */
236   const char *repos_root_url;
237   const char *repos_uuid;
238
239   /* for file and symlink externals */
240   const apr_hash_t *props;
241   apr_array_header_t *iprops;
242   svn_revnum_t changed_rev;
243   apr_time_t changed_date;
244   const char *changed_author;
245   const apr_hash_t *dav_cache;
246
247   /* for inserting files */
248   const svn_checksum_t *checksum;
249
250   /* for inserting symlinks */
251   const char *target;
252
253   const char *record_ancestor_relpath;
254   const char *recorded_repos_relpath;
255   svn_revnum_t recorded_peg_revision;
256   svn_revnum_t recorded_revision;
257
258   /* may need to insert/update ACTUAL to record a conflict  */
259   const svn_skel_t *conflict;
260
261   /* may need to insert/update ACTUAL to record new properties */
262   svn_boolean_t update_actual_props;
263   const apr_hash_t *new_actual_props;
264
265   /* maybe we should copy information from a previous record? */
266   svn_boolean_t keep_recorded_info;
267
268   /* may have work items to queue in this transaction  */
269   const svn_skel_t *work_items;
270
271 } insert_external_baton_t;
272
273
274 /* Forward declarations  */
275 static svn_error_t *
276 add_work_items(svn_sqlite__db_t *sdb,
277                const svn_skel_t *skel,
278                apr_pool_t *scratch_pool);
279
280 static svn_error_t *
281 set_actual_props(svn_wc__db_wcroot_t *wcroot,
282                  const char *local_relpath,
283                  apr_hash_t *props,
284                  apr_pool_t *scratch_pool);
285
286 static svn_error_t *
287 insert_incomplete_children(svn_sqlite__db_t *sdb,
288                            apr_int64_t wc_id,
289                            const char *local_relpath,
290                            apr_int64_t repos_id,
291                            const char *repos_relpath,
292                            svn_revnum_t revision,
293                            const apr_array_header_t *children,
294                            int op_depth,
295                            apr_pool_t *scratch_pool);
296
297 static svn_error_t *
298 db_read_pristine_props(apr_hash_t **props,
299                        svn_wc__db_wcroot_t *wcroot,
300                        const char *local_relpath,
301                        svn_boolean_t deleted_ok,
302                        apr_pool_t *result_pool,
303                        apr_pool_t *scratch_pool);
304
305 static svn_error_t *
306 read_info(svn_wc__db_status_t *status,
307           svn_node_kind_t *kind,
308           svn_revnum_t *revision,
309           const char **repos_relpath,
310           apr_int64_t *repos_id,
311           svn_revnum_t *changed_rev,
312           apr_time_t *changed_date,
313           const char **changed_author,
314           svn_depth_t *depth,
315           const svn_checksum_t **checksum,
316           const char **target,
317           const char **original_repos_relpath,
318           apr_int64_t *original_repos_id,
319           svn_revnum_t *original_revision,
320           svn_wc__db_lock_t **lock,
321           svn_filesize_t *recorded_size,
322           apr_time_t *recorded_time,
323           const char **changelist,
324           svn_boolean_t *conflicted,
325           svn_boolean_t *op_root,
326           svn_boolean_t *had_props,
327           svn_boolean_t *props_mod,
328           svn_boolean_t *have_base,
329           svn_boolean_t *have_more_work,
330           svn_boolean_t *have_work,
331           svn_wc__db_wcroot_t *wcroot,
332           const char *local_relpath,
333           apr_pool_t *result_pool,
334           apr_pool_t *scratch_pool);
335
336 static svn_error_t *
337 scan_addition(svn_wc__db_status_t *status,
338               const char **op_root_relpath,
339               const char **repos_relpath,
340               apr_int64_t *repos_id,
341               const char **original_repos_relpath,
342               apr_int64_t *original_repos_id,
343               svn_revnum_t *original_revision,
344               const char **moved_from_relpath,
345               const char **moved_from_op_root_relpath,
346               int *moved_from_op_depth,
347               svn_wc__db_wcroot_t *wcroot,
348               const char *local_relpath,
349               apr_pool_t *result_pool,
350               apr_pool_t *scratch_pool);
351
352 static svn_error_t *
353 convert_to_working_status(svn_wc__db_status_t *working_status,
354                           svn_wc__db_status_t status);
355
356 static svn_error_t *
357 db_is_switched(svn_boolean_t *is_switched,
358                svn_node_kind_t *kind,
359                svn_wc__db_wcroot_t *wcroot,
360                const char *local_relpath,
361                apr_pool_t *scratch_pool);
362
363
364 /* Return the absolute path, in local path style, of LOCAL_RELPATH
365    in WCROOT.  */
366 static const char *
367 path_for_error_message(const svn_wc__db_wcroot_t *wcroot,
368                        const char *local_relpath,
369                        apr_pool_t *result_pool)
370 {
371   const char *local_abspath
372     = svn_dirent_join(wcroot->abspath, local_relpath, result_pool);
373
374   return svn_dirent_local_style(local_abspath, result_pool);
375 }
376
377
378 /* Return a file size from column SLOT of the SQLITE statement STMT, or
379    SVN_INVALID_FILESIZE if the column value is NULL.  */
380 static svn_filesize_t
381 get_recorded_size(svn_sqlite__stmt_t *stmt, int slot)
382 {
383   if (svn_sqlite__column_is_null(stmt, slot))
384     return SVN_INVALID_FILESIZE;
385   return svn_sqlite__column_int64(stmt, slot);
386 }
387
388
389 /* Return a lock info structure constructed from the given columns of the
390    SQLITE statement STMT, or return NULL if the token column value is null.  */
391 static svn_wc__db_lock_t *
392 lock_from_columns(svn_sqlite__stmt_t *stmt,
393                   int col_token,
394                   int col_owner,
395                   int col_comment,
396                   int col_date,
397                   apr_pool_t *result_pool)
398 {
399   svn_wc__db_lock_t *lock;
400
401   if (svn_sqlite__column_is_null(stmt, col_token))
402     {
403       lock = NULL;
404     }
405   else
406     {
407       lock = apr_pcalloc(result_pool, sizeof(svn_wc__db_lock_t));
408       lock->token = svn_sqlite__column_text(stmt, col_token, result_pool);
409       lock->owner = svn_sqlite__column_text(stmt, col_owner, result_pool);
410       lock->comment = svn_sqlite__column_text(stmt, col_comment, result_pool);
411       lock->date = svn_sqlite__column_int64(stmt, col_date);
412     }
413   return lock;
414 }
415
416
417 svn_error_t *
418 svn_wc__db_fetch_repos_info(const char **repos_root_url,
419                             const char **repos_uuid,
420                             svn_wc__db_wcroot_t *wcroot,
421                             apr_int64_t repos_id,
422                             apr_pool_t *result_pool)
423 {
424   svn_sqlite__stmt_t *stmt;
425   svn_boolean_t have_row;
426
427   if (!repos_root_url && !repos_uuid)
428     return SVN_NO_ERROR;
429
430   if (repos_id == INVALID_REPOS_ID)
431     {
432       if (repos_root_url)
433         *repos_root_url = NULL;
434       if (repos_uuid)
435         *repos_uuid = NULL;
436       return SVN_NO_ERROR;
437     }
438
439   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
440                                     STMT_SELECT_REPOSITORY_BY_ID));
441   SVN_ERR(svn_sqlite__bindf(stmt, "i", repos_id));
442   SVN_ERR(svn_sqlite__step(&have_row, stmt));
443   if (!have_row)
444     return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
445                              _("No REPOSITORY table entry for id '%ld'"),
446                              (long int)repos_id);
447
448   if (repos_root_url)
449     *repos_root_url = svn_sqlite__column_text(stmt, 0, result_pool);
450   if (repos_uuid)
451     *repos_uuid = svn_sqlite__column_text(stmt, 1, result_pool);
452
453   return svn_error_trace(svn_sqlite__reset(stmt));
454 }
455
456 /* Set *REPOS_ID, *REVISION and *REPOS_RELPATH from the given columns of the
457    SQLITE statement STMT, or to NULL/SVN_INVALID_REVNUM if the respective
458    column value is null.  Any of the output parameters may be NULL if not
459    required.  */
460 static void
461 repos_location_from_columns(apr_int64_t *repos_id,
462                             svn_revnum_t *revision,
463                             const char **repos_relpath,
464                             svn_sqlite__stmt_t *stmt,
465                             int col_repos_id,
466                             int col_revision,
467                             int col_repos_relpath,
468                             apr_pool_t *result_pool)
469 {
470   if (repos_id)
471     {
472       /* Fetch repository information via REPOS_ID. */
473       if (svn_sqlite__column_is_null(stmt, col_repos_id))
474         *repos_id = INVALID_REPOS_ID;
475       else
476         *repos_id = svn_sqlite__column_int64(stmt, col_repos_id);
477     }
478   if (revision)
479     {
480       *revision = svn_sqlite__column_revnum(stmt, col_revision);
481     }
482   if (repos_relpath)
483     {
484       *repos_relpath = svn_sqlite__column_text(stmt, col_repos_relpath,
485                                                result_pool);
486     }
487 }
488
489 /* For a given REPOS_ROOT_URL/REPOS_UUID pair, return the existing REPOS_ID
490    value. If one does not exist, then create a new one. */
491 static svn_error_t *
492 create_repos_id(apr_int64_t *repos_id,
493                 const char *repos_root_url,
494                 const char *repos_uuid,
495                 svn_sqlite__db_t *sdb,
496                 apr_pool_t *scratch_pool)
497 {
498   svn_sqlite__stmt_t *get_stmt;
499   svn_sqlite__stmt_t *insert_stmt;
500   svn_boolean_t have_row;
501
502   SVN_ERR(svn_sqlite__get_statement(&get_stmt, sdb, STMT_SELECT_REPOSITORY));
503   SVN_ERR(svn_sqlite__bindf(get_stmt, "s", repos_root_url));
504   SVN_ERR(svn_sqlite__step(&have_row, get_stmt));
505
506   if (have_row)
507     {
508       *repos_id = svn_sqlite__column_int64(get_stmt, 0);
509       return svn_error_trace(svn_sqlite__reset(get_stmt));
510     }
511   SVN_ERR(svn_sqlite__reset(get_stmt));
512
513   /* NOTE: strictly speaking, there is a race condition between the
514      above query and the insertion below. We're simply going to ignore
515      that, as it means two processes are *modifying* the working copy
516      at the same time, *and* new repositores are becoming visible.
517      This is rare enough, let alone the miniscule chance of hitting
518      this race condition. Further, simply failing out will leave the
519      database in a consistent state, and the user can just re-run the
520      failed operation. */
521
522   SVN_ERR(svn_sqlite__get_statement(&insert_stmt, sdb,
523                                     STMT_INSERT_REPOSITORY));
524   SVN_ERR(svn_sqlite__bindf(insert_stmt, "ss", repos_root_url, repos_uuid));
525   return svn_error_trace(svn_sqlite__insert(repos_id, insert_stmt));
526 }
527
528
529 /* Initialize the baton with appropriate "blank" values. This allows the
530    insertion function to leave certain columns null.  */
531 static void
532 blank_ibb(insert_base_baton_t *pibb)
533 {
534   memset(pibb, 0, sizeof(*pibb));
535   pibb->revision = SVN_INVALID_REVNUM;
536   pibb->changed_rev = SVN_INVALID_REVNUM;
537   pibb->depth = svn_depth_infinity;
538   pibb->repos_id = INVALID_REPOS_ID;
539 }
540
541
542 /* Extend any delete of the parent of LOCAL_RELPATH to LOCAL_RELPATH.
543
544    ### What about KIND and OP_DEPTH?  KIND ought to be redundant; I'm
545        discussing on dev@ whether we can let that be null for presence
546        == base-deleted.  OP_DEPTH is the op-depth of what, and why?
547        It is used to select the lowest working node higher than OP_DEPTH,
548        so, in terms of the API, OP_DEPTH means ...?
549
550    Given a wc:
551
552               0         1         2         3         4
553               normal
554    A          normal
555    A/B        normal              normal
556    A/B/C                          not-pres  normal
557    A/B/C/D                                            normal
558
559    That is checkout, delete A/B, copy a replacement A/B, delete copied
560    child A/B/C, add replacement A/B/C, add A/B/C/D.
561
562    Now an update that adds base nodes for A/B/C, A/B/C/D and A/B/C/D/E
563    must extend the A/B deletion:
564
565               0         1         2         3         4
566               normal
567    A          normal
568    A/B        normal              normal
569    A/B/C      normal              not-pres  normal
570    A/B/C/D    normal              base-del            normal
571    A/B/C/D/E  normal              base-del
572
573    When adding a node if the parent has a higher working node then the
574    parent node is deleted (or replaced) and the delete must be extended
575    to cover new node.
576
577    In the example above A/B/C/D and A/B/C/D/E are the nodes that get
578    the extended delete, A/B/C is already deleted.
579
580    If ADDED_DELETE is not NULL, set *ADDED_DELETE to TRUE if a new delete
581    was recorded, otherwise to FALSE.
582  */
583 static svn_error_t *
584 db_extend_parent_delete(svn_wc__db_wcroot_t *wcroot,
585                         const char *local_relpath,
586                         svn_node_kind_t kind,
587                         int op_depth,
588                         apr_pool_t *scratch_pool)
589 {
590   svn_boolean_t have_row;
591   svn_sqlite__stmt_t *stmt;
592   int parent_op_depth;
593   const char *parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
594
595   SVN_ERR_ASSERT(local_relpath[0]);
596
597   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
598                                     STMT_SELECT_LOWEST_WORKING_NODE));
599   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, parent_relpath,
600                             op_depth));
601   SVN_ERR(svn_sqlite__step(&have_row, stmt));
602   if (have_row)
603     parent_op_depth = svn_sqlite__column_int(stmt, 0);
604   SVN_ERR(svn_sqlite__reset(stmt));
605   if (have_row)
606     {
607       int existing_op_depth;
608
609       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
610                                 op_depth));
611       SVN_ERR(svn_sqlite__step(&have_row, stmt));
612       if (have_row)
613         existing_op_depth = svn_sqlite__column_int(stmt, 0);
614       SVN_ERR(svn_sqlite__reset(stmt));
615       if (!have_row || parent_op_depth < existing_op_depth)
616         {
617           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
618                               STMT_INSTALL_WORKING_NODE_FOR_DELETE));
619           SVN_ERR(svn_sqlite__bindf(stmt, "isdst", wcroot->wc_id,
620                                     local_relpath, parent_op_depth,
621                                     parent_relpath, kind_map, kind));
622           SVN_ERR(svn_sqlite__update(NULL, stmt));
623         }
624     }
625
626   return SVN_NO_ERROR;
627 }
628
629
630 /* This is the reverse of db_extend_parent_delete.
631
632    When removing a node if the parent has a higher working node then
633    the parent node and this node are both deleted or replaced and any
634    delete over this node must be removed.
635
636    This function (like most wcroot functions) assumes that its caller
637    only uses this function within an sqlite transaction if atomic
638    behavior is needed.
639  */
640 static svn_error_t *
641 db_retract_parent_delete(svn_wc__db_wcroot_t *wcroot,
642                          const char *local_relpath,
643                          int op_depth,
644                          apr_pool_t *scratch_pool)
645 {
646   svn_sqlite__stmt_t *stmt;
647   svn_boolean_t have_row;
648   int working_depth;
649   svn_wc__db_status_t presence;
650   const char *moved_to;
651
652   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
653                                     STMT_SELECT_LOWEST_WORKING_NODE));
654   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
655                             op_depth));
656   SVN_ERR(svn_sqlite__step(&have_row, stmt));
657
658   if (!have_row)
659     return svn_error_trace(svn_sqlite__reset(stmt));
660
661   working_depth = svn_sqlite__column_int(stmt, 0);
662   presence = svn_sqlite__column_token(stmt, 1, presence_map);
663   moved_to = svn_sqlite__column_text(stmt, 3, scratch_pool);
664
665   SVN_ERR(svn_sqlite__reset(stmt));
666
667   if (moved_to)
668     {
669       /* Turn the move into a copy to keep the NODES table valid */
670       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
671                                         STMT_CLEAR_MOVED_HERE_RECURSIVE));
672       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
673                                 moved_to, relpath_depth(moved_to)));
674       SVN_ERR(svn_sqlite__step_done(stmt));
675
676       /* This leaves just the moved_to information on the origin,
677          which we will remove in the next step */
678     }
679
680   if (presence == svn_wc__db_status_base_deleted)
681     {
682       /* Nothing left to shadow; remove the base-deleted node */
683       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_NODE));
684     }
685   else if (moved_to)
686     {
687       /* Clear moved to information, as this node is no longer base-deleted */
688       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
689                                         STMT_CLEAR_MOVED_TO_RELPATH));
690       }
691   else
692     {
693       /* Nothing to update */
694       return SVN_NO_ERROR;
695     }
696
697   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
698                             working_depth));
699
700   return svn_error_trace(svn_sqlite__update(NULL, stmt));
701 }
702
703
704
705 /* Insert the base row represented by (insert_base_baton_t *) BATON. */
706 static svn_error_t *
707 insert_base_node(const insert_base_baton_t *pibb,
708                  svn_wc__db_wcroot_t *wcroot,
709                  const char *local_relpath,
710                  apr_pool_t *scratch_pool)
711 {
712   apr_int64_t repos_id = pibb->repos_id;
713   svn_sqlite__stmt_t *stmt;
714   svn_filesize_t recorded_size = SVN_INVALID_FILESIZE;
715   apr_int64_t recorded_time;
716   svn_boolean_t present;
717
718   /* The directory at the WCROOT has a NULL parent_relpath. Otherwise,
719      bind the appropriate parent_relpath. */
720   const char *parent_relpath =
721     (*local_relpath == '\0') ? NULL
722     : svn_relpath_dirname(local_relpath, scratch_pool);
723
724   if (pibb->repos_id == INVALID_REPOS_ID)
725     SVN_ERR(create_repos_id(&repos_id, pibb->repos_root_url, pibb->repos_uuid,
726                             wcroot->sdb, scratch_pool));
727
728   SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
729   SVN_ERR_ASSERT(pibb->repos_relpath != NULL);
730
731   if (pibb->keep_recorded_info)
732     {
733       svn_boolean_t have_row;
734       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
735                                         STMT_SELECT_BASE_NODE));
736       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
737       SVN_ERR(svn_sqlite__step(&have_row, stmt));
738       if (have_row)
739         {
740           /* Preserve size and modification time if caller asked us to. */
741           recorded_size = get_recorded_size(stmt, 6);
742           recorded_time = svn_sqlite__column_int64(stmt, 12);
743         }
744       SVN_ERR(svn_sqlite__reset(stmt));
745     }
746
747   present = (pibb->status == svn_wc__db_status_normal
748              || pibb->status == svn_wc__db_status_incomplete);
749
750   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
751   SVN_ERR(svn_sqlite__bindf(stmt, "isdsisr"
752                             "tstr"               /* 8 - 11 */
753                             "isnnnnns",          /* 12 - 19 */
754                             wcroot->wc_id,       /* 1 */
755                             local_relpath,       /* 2 */
756                             0,              /* op_depth is 0 for base */
757                             parent_relpath,      /* 4 */
758                             repos_id,
759                             pibb->repos_relpath,
760                             pibb->revision,
761                             presence_map, pibb->status, /* 8 */
762                             (pibb->kind == svn_node_dir && present) /* 9 */
763                               ? svn_token__to_word(depth_map, pibb->depth)
764                               : NULL,
765                             kind_map, pibb->kind, /* 10 */
766                             pibb->changed_rev,    /* 11 */
767                             pibb->changed_date,   /* 12 */
768                             pibb->changed_author, /* 13 */
769                             (pibb->kind == svn_node_symlink && present) ?
770                                 pibb->target : NULL)); /* 19 */
771   if (pibb->kind == svn_node_file && present)
772     {
773       if (!pibb->checksum
774           && pibb->status != svn_wc__db_status_not_present
775           && pibb->status != svn_wc__db_status_excluded
776           && pibb->status != svn_wc__db_status_server_excluded)
777         return svn_error_createf(SVN_ERR_WC_CORRUPT, svn_sqlite__reset(stmt),
778                                  _("The file '%s' has no checksum."),
779                                  path_for_error_message(wcroot, local_relpath,
780                                                         scratch_pool));
781
782       SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, pibb->checksum,
783                                         scratch_pool));
784
785       if (recorded_size != SVN_INVALID_FILESIZE)
786         {
787           SVN_ERR(svn_sqlite__bind_int64(stmt, 16, recorded_size));
788           SVN_ERR(svn_sqlite__bind_int64(stmt, 17, recorded_time));
789         }
790     }
791
792   /* Set properties.  Must be null if presence not normal or incomplete. */
793   assert(pibb->status == svn_wc__db_status_normal
794          || pibb->status == svn_wc__db_status_incomplete
795          || pibb->props == NULL);
796   if (present)
797     {
798       SVN_ERR(svn_sqlite__bind_properties(stmt, 15, pibb->props,
799                                           scratch_pool));
800
801       SVN_ERR(svn_sqlite__bind_iprops(stmt, 23, pibb->iprops,
802                                       scratch_pool));
803     }
804
805   if (pibb->dav_cache)
806     SVN_ERR(svn_sqlite__bind_properties(stmt, 18, pibb->dav_cache,
807                                         scratch_pool));
808
809   if (pibb->file_external)
810     SVN_ERR(svn_sqlite__bind_int(stmt, 20, 1));
811
812   SVN_ERR(svn_sqlite__insert(NULL, stmt));
813
814   if (pibb->update_actual_props)
815     {
816       /* Cast away const, to allow calling property helpers */
817       apr_hash_t *base_props = (apr_hash_t *)pibb->props;
818       apr_hash_t *new_actual_props = (apr_hash_t *)pibb->new_actual_props;
819
820       if (base_props != NULL
821           && new_actual_props != NULL
822           && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
823         {
824           apr_array_header_t *diffs;
825
826           SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
827                                  scratch_pool));
828
829           if (diffs->nelts == 0)
830             new_actual_props = NULL;
831         }
832
833       SVN_ERR(set_actual_props(wcroot, local_relpath, new_actual_props,
834                                scratch_pool));
835     }
836
837   if (pibb->kind == svn_node_dir && pibb->children)
838     SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
839                                        local_relpath,
840                                        repos_id,
841                                        pibb->repos_relpath,
842                                        pibb->revision,
843                                        pibb->children,
844                                        0 /* BASE */,
845                                        scratch_pool));
846
847   /* When this is not the root node, check shadowing behavior */
848   if (*local_relpath)
849     {
850       if (parent_relpath
851           && ((pibb->status == svn_wc__db_status_normal)
852               || (pibb->status == svn_wc__db_status_incomplete))
853           && ! pibb->file_external)
854         {
855           SVN_ERR(db_extend_parent_delete(wcroot, local_relpath,
856                                           pibb->kind, 0,
857                                           scratch_pool));
858         }
859       else if (pibb->status == svn_wc__db_status_not_present
860                || pibb->status == svn_wc__db_status_server_excluded
861                || pibb->status == svn_wc__db_status_excluded)
862         {
863           SVN_ERR(db_retract_parent_delete(wcroot, local_relpath, 0,
864                                            scratch_pool));
865         }
866     }
867
868   if (pibb->delete_working)
869     {
870       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
871                                     STMT_DELETE_WORKING_NODE));
872       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
873       SVN_ERR(svn_sqlite__step_done(stmt));
874     }
875   if (pibb->insert_base_deleted)
876     {
877       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
878                                         STMT_INSERT_DELETE_FROM_BASE));
879       SVN_ERR(svn_sqlite__bindf(stmt, "isd",
880                                 wcroot->wc_id, local_relpath,
881                                 relpath_depth(local_relpath)));
882       SVN_ERR(svn_sqlite__step_done(stmt));
883     }
884
885   SVN_ERR(add_work_items(wcroot->sdb, pibb->work_items, scratch_pool));
886   if (pibb->conflict)
887     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
888                                               pibb->conflict, scratch_pool));
889
890   return SVN_NO_ERROR;
891 }
892
893
894 /* Initialize the baton with appropriate "blank" values. This allows the
895    insertion function to leave certain columns null.  */
896 static void
897 blank_iwb(insert_working_baton_t *piwb)
898 {
899   memset(piwb, 0, sizeof(*piwb));
900   piwb->changed_rev = SVN_INVALID_REVNUM;
901   piwb->depth = svn_depth_infinity;
902
903   /* ORIGINAL_REPOS_ID and ORIGINAL_REVNUM could use some kind of "nil"
904      value, but... meh. We'll avoid them if ORIGINAL_REPOS_RELPATH==NULL.  */
905 }
906
907
908 /* Insert a row in NODES for each (const char *) child name in CHILDREN,
909    whose parent directory is LOCAL_RELPATH, at op_depth=OP_DEPTH.  Set each
910    child's presence to 'incomplete', kind to 'unknown', repos_id to REPOS_ID,
911    repos_path by appending the child name to REPOS_PATH, and revision to
912    REVISION (which should match the parent's revision).
913
914    If REPOS_ID is INVALID_REPOS_ID, set each child's repos_id to null. */
915 static svn_error_t *
916 insert_incomplete_children(svn_sqlite__db_t *sdb,
917                            apr_int64_t wc_id,
918                            const char *local_relpath,
919                            apr_int64_t repos_id,
920                            const char *repos_path,
921                            svn_revnum_t revision,
922                            const apr_array_header_t *children,
923                            int op_depth,
924                            apr_pool_t *scratch_pool)
925 {
926   svn_sqlite__stmt_t *stmt;
927   int i;
928   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
929   apr_hash_t *moved_to_relpaths = apr_hash_make(scratch_pool);
930
931   SVN_ERR_ASSERT(repos_path != NULL || op_depth > 0);
932   SVN_ERR_ASSERT((repos_id != INVALID_REPOS_ID)
933                  == (repos_path != NULL));
934
935   /* If we're inserting WORKING nodes, we might be replacing existing
936    * nodes which were moved-away. We need to retain the moved-to relpath of
937    * such nodes in order not to lose move information during replace. */
938   if (op_depth > 0)
939     {
940       for (i = children->nelts; i--; )
941         {
942           const char *name = APR_ARRAY_IDX(children, i, const char *);
943           svn_boolean_t have_row;
944
945           svn_pool_clear(iterpool);
946
947           SVN_ERR(svn_sqlite__get_statement(&stmt, sdb,
948                                             STMT_SELECT_WORKING_NODE));
949           SVN_ERR(svn_sqlite__bindf(stmt, "is", wc_id,
950                                     svn_relpath_join(local_relpath, name,
951                                                      iterpool)));
952           SVN_ERR(svn_sqlite__step(&have_row, stmt));
953           if (have_row && !svn_sqlite__column_is_null(stmt, 14))
954             svn_hash_sets(moved_to_relpaths, name,
955                           svn_sqlite__column_text(stmt, 14, scratch_pool));
956
957           SVN_ERR(svn_sqlite__reset(stmt));
958         }
959     }
960
961   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_NODE));
962
963   for (i = children->nelts; i--; )
964     {
965       const char *name = APR_ARRAY_IDX(children, i, const char *);
966
967       svn_pool_clear(iterpool);
968
969       SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnrsnsnnnnnnnnnnsn",
970                                 wc_id,
971                                 svn_relpath_join(local_relpath, name,
972                                                  iterpool),
973                                 op_depth,
974                                 local_relpath,
975                                 revision,
976                                 "incomplete", /* 8, presence */
977                                 "unknown",    /* 10, kind */
978                                 /* 21, moved_to */
979                                 svn_hash_gets(moved_to_relpaths, name)));
980       if (repos_id != INVALID_REPOS_ID)
981         {
982           SVN_ERR(svn_sqlite__bind_int64(stmt, 5, repos_id));
983           SVN_ERR(svn_sqlite__bind_text(stmt, 6,
984                                         svn_relpath_join(repos_path, name,
985                                                          iterpool)));
986         }
987
988       SVN_ERR(svn_sqlite__insert(NULL, stmt));
989     }
990
991   svn_pool_destroy(iterpool);
992
993   return SVN_NO_ERROR;
994 }
995
996
997 /* Insert the working row represented by (insert_working_baton_t *) BATON. */
998 static svn_error_t *
999 insert_working_node(const insert_working_baton_t *piwb,
1000                     svn_wc__db_wcroot_t *wcroot,
1001                     const char *local_relpath,
1002                     apr_pool_t *scratch_pool)
1003 {
1004   const char *parent_relpath;
1005   const char *moved_to_relpath = NULL;
1006   svn_sqlite__stmt_t *stmt;
1007   svn_boolean_t have_row;
1008   svn_boolean_t present;
1009
1010   SVN_ERR_ASSERT(piwb->op_depth > 0);
1011
1012   /* We cannot insert a WORKING_NODE row at the wcroot.  */
1013   SVN_ERR_ASSERT(*local_relpath != '\0');
1014   parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
1015
1016   /* Preserve existing moved-to information for this relpath,
1017    * which might exist in case we're replacing an existing base-deleted
1018    * node. */
1019   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_MOVED_TO));
1020   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
1021                             piwb->op_depth));
1022   SVN_ERR(svn_sqlite__step(&have_row, stmt));
1023   if (have_row)
1024     moved_to_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
1025   SVN_ERR(svn_sqlite__reset(stmt));
1026
1027   present = (piwb->presence == svn_wc__db_status_normal
1028              || piwb->presence == svn_wc__db_status_incomplete);
1029
1030   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_NODE));
1031   SVN_ERR(svn_sqlite__bindf(stmt, "isdsnnntstrisn"
1032                 "nnnn" /* properties translated_size last_mod_time dav_cache */
1033                 "sns", /* symlink_target, file_external, moved_to */
1034                 wcroot->wc_id, local_relpath,
1035                 piwb->op_depth,
1036                 parent_relpath,
1037                 presence_map, piwb->presence,
1038                 (piwb->kind == svn_node_dir && present)
1039                             ? svn_token__to_word(depth_map, piwb->depth) : NULL,
1040                 kind_map, piwb->kind,
1041                 piwb->changed_rev,
1042                 piwb->changed_date,
1043                 piwb->changed_author,
1044                 /* Note: incomplete nodes may have a NULL target.  */
1045                 (piwb->kind == svn_node_symlink && present)
1046                             ? piwb->target : NULL,
1047                 moved_to_relpath));
1048
1049   if (piwb->moved_here)
1050     {
1051       SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
1052     }
1053
1054   if (piwb->kind == svn_node_file && present)
1055     {
1056       SVN_ERR(svn_sqlite__bind_checksum(stmt, 14, piwb->checksum,
1057                                         scratch_pool));
1058     }
1059
1060   if (piwb->original_repos_relpath != NULL)
1061     {
1062       SVN_ERR(svn_sqlite__bind_int64(stmt, 5, piwb->original_repos_id));
1063       SVN_ERR(svn_sqlite__bind_text(stmt, 6, piwb->original_repos_relpath));
1064       SVN_ERR(svn_sqlite__bind_revnum(stmt, 7, piwb->original_revnum));
1065     }
1066
1067   /* Set properties.  Must be null if presence not normal or incomplete. */
1068   assert(piwb->presence == svn_wc__db_status_normal
1069          || piwb->presence == svn_wc__db_status_incomplete
1070          || piwb->props == NULL);
1071   if (present && piwb->original_repos_relpath)
1072     SVN_ERR(svn_sqlite__bind_properties(stmt, 15, piwb->props, scratch_pool));
1073
1074   SVN_ERR(svn_sqlite__insert(NULL, stmt));
1075
1076   /* Insert incomplete children, if specified.
1077      The children are part of the same op and so have the same op_depth.
1078      (The only time we'd want a different depth is during a recursive
1079      simple add, but we never insert children here during a simple add.) */
1080   if (piwb->kind == svn_node_dir && piwb->children)
1081     SVN_ERR(insert_incomplete_children(wcroot->sdb, wcroot->wc_id,
1082                                        local_relpath,
1083                                        INVALID_REPOS_ID /* inherit repos_id */,
1084                                        NULL /* inherit repos_path */,
1085                                        piwb->original_revnum,
1086                                        piwb->children,
1087                                        piwb->op_depth,
1088                                        scratch_pool));
1089
1090   if (piwb->update_actual_props)
1091     {
1092       /* Cast away const, to allow calling property helpers */
1093       apr_hash_t *base_props = (apr_hash_t *)piwb->props;
1094       apr_hash_t *new_actual_props = (apr_hash_t *)piwb->new_actual_props;
1095
1096       if (base_props != NULL
1097           && new_actual_props != NULL
1098           && (apr_hash_count(base_props) == apr_hash_count(new_actual_props)))
1099         {
1100           apr_array_header_t *diffs;
1101
1102           SVN_ERR(svn_prop_diffs(&diffs, new_actual_props, base_props,
1103                                  scratch_pool));
1104
1105           if (diffs->nelts == 0)
1106             new_actual_props = NULL;
1107         }
1108
1109       SVN_ERR(set_actual_props(wcroot, local_relpath, new_actual_props,
1110                                scratch_pool));
1111     }
1112
1113   if (piwb->kind == svn_node_dir)
1114     {
1115       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1116                                         STMT_UPDATE_ACTUAL_CLEAR_CHANGELIST));
1117       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1118       SVN_ERR(svn_sqlite__step_done(stmt));
1119
1120       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1121                                         STMT_DELETE_ACTUAL_EMPTY));
1122       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1123       SVN_ERR(svn_sqlite__step_done(stmt));
1124     }
1125
1126   if (piwb->not_present_op_depth > 0
1127       && piwb->not_present_op_depth < piwb->op_depth)
1128     {
1129       /* And also insert a not-present node to tell the commit processing that
1130          a child of the parent node was not copied. */
1131       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
1132                                         STMT_INSERT_NODE));
1133
1134       SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
1135                                 wcroot->wc_id, local_relpath,
1136                                 piwb->not_present_op_depth, parent_relpath,
1137                                 piwb->original_repos_id,
1138                                 piwb->original_repos_relpath,
1139                                 piwb->original_revnum,
1140                                 presence_map, svn_wc__db_status_not_present,
1141                                 /* NULL */
1142                                 kind_map, piwb->kind));
1143
1144       SVN_ERR(svn_sqlite__step_done(stmt));
1145     }
1146
1147   SVN_ERR(add_work_items(wcroot->sdb, piwb->work_items, scratch_pool));
1148   if (piwb->conflict)
1149     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
1150                                               piwb->conflict, scratch_pool));
1151
1152   return SVN_NO_ERROR;
1153 }
1154
1155
1156 /* Return in *CHILDREN all of the children of the directory LOCAL_RELPATH,
1157    of any status, in all op-depths in the NODES table. */
1158 static svn_error_t *
1159 gather_children(const apr_array_header_t **children,
1160                 svn_wc__db_wcroot_t *wcroot,
1161                 const char *parent_relpath,
1162                 int stmt_idx,
1163                 int op_depth,
1164                 apr_pool_t *result_pool,
1165                 apr_pool_t *scratch_pool)
1166 {
1167   apr_array_header_t *result;
1168   svn_sqlite__stmt_t *stmt;
1169   svn_boolean_t have_row;
1170
1171   result = apr_array_make(result_pool, 16, sizeof(const char*));
1172
1173   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
1174   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
1175   if (op_depth >= 0)
1176     SVN_ERR(svn_sqlite__bind_int(stmt, 3, op_depth));
1177
1178   SVN_ERR(svn_sqlite__step(&have_row, stmt));
1179   while (have_row)
1180     {
1181       const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
1182       const char *name = svn_relpath_basename(child_relpath, result_pool);
1183
1184       APR_ARRAY_PUSH(result, const char *) = name;
1185
1186       SVN_ERR(svn_sqlite__step(&have_row, stmt));
1187     }
1188
1189   SVN_ERR(svn_sqlite__reset(stmt));
1190   *children = result;
1191   return SVN_NO_ERROR;
1192 }
1193
1194 /* Return TRUE if CHILD_ABSPATH is an immediate child of PARENT_ABSPATH.
1195  * Else, return FALSE. */
1196 static svn_boolean_t
1197 is_immediate_child_path(const char *parent_abspath, const char *child_abspath)
1198 {
1199   const char *local_relpath = svn_dirent_skip_ancestor(parent_abspath,
1200                                                        child_abspath);
1201
1202   /* To be an immediate child local_relpath should have one (not empty)
1203      component */
1204   return local_relpath && *local_relpath && !strchr(local_relpath, '/');
1205 }
1206
1207
1208 /* Remove the access baton for LOCAL_ABSPATH from ACCESS_CACHE. */
1209 static void
1210 remove_from_access_cache(apr_hash_t *access_cache,
1211                          const char *local_abspath)
1212 {
1213   svn_wc_adm_access_t *adm_access;
1214
1215   adm_access = svn_hash_gets(access_cache, local_abspath);
1216   if (adm_access)
1217     svn_wc__adm_access_set_entries(adm_access, NULL);
1218 }
1219
1220
1221 /* Flush the access baton for LOCAL_ABSPATH, and any of its children up to
1222  * the specified DEPTH, from the access baton cache in WCROOT.
1223  * Also flush the access baton for the parent of LOCAL_ABSPATH.I
1224  *
1225  * This function must be called when the access baton cache goes stale,
1226  * i.e. data about LOCAL_ABSPATH will need to be read again from disk.
1227  *
1228  * Use SCRATCH_POOL for temporary allocations. */
1229 static svn_error_t *
1230 flush_entries(svn_wc__db_wcroot_t *wcroot,
1231               const char *local_abspath,
1232               svn_depth_t depth,
1233               apr_pool_t *scratch_pool)
1234 {
1235   const char *parent_abspath;
1236
1237   if (apr_hash_count(wcroot->access_cache) == 0)
1238     return SVN_NO_ERROR;
1239
1240   remove_from_access_cache(wcroot->access_cache, local_abspath);
1241
1242   if (depth > svn_depth_empty)
1243     {
1244       apr_hash_index_t *hi;
1245
1246       /* Flush access batons of children within the specified depth. */
1247       for (hi = apr_hash_first(scratch_pool, wcroot->access_cache);
1248            hi;
1249            hi = apr_hash_next(hi))
1250         {
1251           const char *item_abspath = apr_hash_this_key(hi);
1252
1253           if ((depth == svn_depth_files || depth == svn_depth_immediates) &&
1254               is_immediate_child_path(local_abspath, item_abspath))
1255             {
1256               remove_from_access_cache(wcroot->access_cache, item_abspath);
1257             }
1258           else if (depth == svn_depth_infinity &&
1259                    svn_dirent_is_ancestor(local_abspath, item_abspath))
1260             {
1261               remove_from_access_cache(wcroot->access_cache, item_abspath);
1262             }
1263         }
1264     }
1265
1266   /* We're going to be overly aggressive here and just flush the parent
1267      without doing much checking.  This may hurt performance for
1268      legacy API consumers, but that's not our problem. :) */
1269   parent_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
1270   remove_from_access_cache(wcroot->access_cache, parent_abspath);
1271
1272   return SVN_NO_ERROR;
1273 }
1274
1275
1276 /* Add a single WORK_ITEM into the given SDB's WORK_QUEUE table. This does
1277    not perform its work within a transaction, assuming the caller will
1278    manage that.  */
1279 static svn_error_t *
1280 add_single_work_item(svn_sqlite__db_t *sdb,
1281                      const svn_skel_t *work_item,
1282                      apr_pool_t *scratch_pool)
1283 {
1284   svn_stringbuf_t *serialized;
1285   svn_sqlite__stmt_t *stmt;
1286
1287   serialized = svn_skel__unparse(work_item, scratch_pool);
1288   SVN_ERR(svn_sqlite__get_statement(&stmt, sdb, STMT_INSERT_WORK_ITEM));
1289   SVN_ERR(svn_sqlite__bind_blob(stmt, 1, serialized->data, serialized->len));
1290   return svn_error_trace(svn_sqlite__insert(NULL, stmt));
1291 }
1292
1293
1294 /* Add work item(s) to the given SDB. Also see add_single_work_item(). This
1295    SKEL is usually passed to the various wc_db operation functions. It may
1296    be NULL, indicating no additional work items are needed, it may be a
1297    single work item, or it may be a list of work items.  */
1298 static svn_error_t *
1299 add_work_items(svn_sqlite__db_t *sdb,
1300                const svn_skel_t *skel,
1301                apr_pool_t *scratch_pool)
1302 {
1303   apr_pool_t *iterpool;
1304
1305   /* Maybe there are no work items to insert.  */
1306   if (skel == NULL)
1307     return SVN_NO_ERROR;
1308
1309   /* Should have a list.  */
1310   SVN_ERR_ASSERT(!skel->is_atom);
1311
1312   /* Is the list a single work item? Or a list of work items?  */
1313   if (SVN_WC__SINGLE_WORK_ITEM(skel))
1314     return svn_error_trace(add_single_work_item(sdb, skel, scratch_pool));
1315
1316   /* SKEL is a list-of-lists, aka list of work items.  */
1317
1318   iterpool = svn_pool_create(scratch_pool);
1319   for (skel = skel->children; skel; skel = skel->next)
1320     {
1321       svn_pool_clear(iterpool);
1322
1323       SVN_ERR(add_single_work_item(sdb, skel, iterpool));
1324     }
1325   svn_pool_destroy(iterpool);
1326
1327   return SVN_NO_ERROR;
1328 }
1329
1330
1331 /* Determine whether the node exists for a given WCROOT and LOCAL_RELPATH.  */
1332 static svn_error_t *
1333 does_node_exist(svn_boolean_t *exists,
1334                 const svn_wc__db_wcroot_t *wcroot,
1335                 const char *local_relpath)
1336 {
1337   svn_sqlite__stmt_t *stmt;
1338
1339   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DOES_NODE_EXIST));
1340   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
1341   SVN_ERR(svn_sqlite__step(exists, stmt));
1342
1343   return svn_error_trace(svn_sqlite__reset(stmt));
1344 }
1345
1346 svn_error_t *
1347 svn_wc__db_install_schema_statistics(svn_sqlite__db_t *sdb,
1348                                      apr_pool_t *scratch_pool)
1349 {
1350   SVN_ERR(svn_sqlite__exec_statements(sdb, STMT_INSTALL_SCHEMA_STATISTICS));
1351
1352   return SVN_NO_ERROR;
1353 }
1354
1355 /* Helper for create_db(). Initializes our wc.db schema.
1356  */
1357 static svn_error_t *
1358 init_db(/* output values */
1359         apr_int64_t *repos_id,
1360         apr_int64_t *wc_id,
1361         /* input values */
1362         svn_sqlite__db_t *db,
1363         const char *repos_root_url,
1364         const char *repos_uuid,
1365         const char *root_node_repos_relpath,
1366         svn_revnum_t root_node_revision,
1367         svn_depth_t root_node_depth,
1368         apr_pool_t *scratch_pool)
1369 {
1370   svn_sqlite__stmt_t *stmt;
1371
1372   /* Create the database's schema.  */
1373   SVN_ERR(svn_sqlite__exec_statements(db, STMT_CREATE_SCHEMA));
1374
1375   SVN_ERR(svn_wc__db_install_schema_statistics(db, scratch_pool));
1376
1377   /* Insert the repository. */
1378   SVN_ERR(create_repos_id(repos_id, repos_root_url, repos_uuid,
1379                           db, scratch_pool));
1380
1381   /* Insert the wcroot. */
1382   /* ### Right now, this just assumes wc metadata is being stored locally. */
1383   SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_WCROOT));
1384   SVN_ERR(svn_sqlite__insert(wc_id, stmt));
1385
1386   if (root_node_repos_relpath)
1387     {
1388       svn_wc__db_status_t status = svn_wc__db_status_normal;
1389
1390       if (root_node_revision > 0)
1391         status = svn_wc__db_status_incomplete; /* Will be filled by update */
1392
1393       SVN_ERR(svn_sqlite__get_statement(&stmt, db, STMT_INSERT_NODE));
1394       SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtst",
1395                                 *wc_id,              /* 1 */
1396                                 "",                  /* 2 */
1397                                 0,                   /* op_depth is 0 for base */
1398                                 SVN_VA_NULL,         /* 4 */
1399                                 *repos_id,
1400                                 root_node_repos_relpath,
1401                                 root_node_revision,
1402                                 presence_map, status, /* 8 */
1403                                 svn_token__to_word(depth_map,
1404                                                    root_node_depth),
1405                                 kind_map, svn_node_dir /* 10 */));
1406
1407       SVN_ERR(svn_sqlite__insert(NULL, stmt));
1408     }
1409
1410   return SVN_NO_ERROR;
1411 }
1412
1413 /* Create an sqlite database at DIR_ABSPATH/SDB_FNAME and insert
1414    records for REPOS_ID (using REPOS_ROOT_URL and REPOS_UUID) into
1415    REPOSITORY and for WC_ID into WCROOT.  Return the DB connection
1416    in *SDB.
1417
1418    If ROOT_NODE_REPOS_RELPATH is not NULL, insert a BASE node at
1419    the working copy root with repository relpath ROOT_NODE_REPOS_RELPATH,
1420    revision ROOT_NODE_REVISION and depth ROOT_NODE_DEPTH.
1421    */
1422 static svn_error_t *
1423 create_db(svn_sqlite__db_t **sdb,
1424           apr_int64_t *repos_id,
1425           apr_int64_t *wc_id,
1426           const char *dir_abspath,
1427           const char *repos_root_url,
1428           const char *repos_uuid,
1429           const char *sdb_fname,
1430           const char *root_node_repos_relpath,
1431           svn_revnum_t root_node_revision,
1432           svn_depth_t root_node_depth,
1433           svn_boolean_t exclusive,
1434           apr_int32_t timeout,
1435           apr_pool_t *result_pool,
1436           apr_pool_t *scratch_pool)
1437 {
1438   SVN_ERR(svn_wc__db_util_open_db(sdb, dir_abspath, sdb_fname,
1439                                   svn_sqlite__mode_rwcreate, exclusive,
1440                                   timeout,
1441                                   NULL /* my_statements */,
1442                                   result_pool, scratch_pool));
1443
1444   SVN_SQLITE__WITH_LOCK(init_db(repos_id, wc_id,
1445                                 *sdb, repos_root_url, repos_uuid,
1446                                 root_node_repos_relpath, root_node_revision,
1447                                 root_node_depth, scratch_pool),
1448                         *sdb);
1449
1450   return SVN_NO_ERROR;
1451 }
1452
1453
1454 svn_error_t *
1455 svn_wc__db_init(svn_wc__db_t *db,
1456                 const char *local_abspath,
1457                 const char *repos_relpath,
1458                 const char *repos_root_url,
1459                 const char *repos_uuid,
1460                 svn_revnum_t initial_rev,
1461                 svn_depth_t depth,
1462                 apr_pool_t *scratch_pool)
1463 {
1464   svn_sqlite__db_t *sdb;
1465   apr_int64_t repos_id;
1466   apr_int64_t wc_id;
1467   svn_wc__db_wcroot_t *wcroot;
1468   svn_boolean_t sqlite_exclusive = FALSE;
1469   apr_int32_t sqlite_timeout = 0; /* default timeout */
1470   apr_hash_index_t *hi;
1471
1472   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1473   SVN_ERR_ASSERT(repos_relpath != NULL);
1474   SVN_ERR_ASSERT(depth == svn_depth_empty
1475                  || depth == svn_depth_files
1476                  || depth == svn_depth_immediates
1477                  || depth == svn_depth_infinity);
1478
1479   /* ### REPOS_ROOT_URL and REPOS_UUID may be NULL. ... more doc: tbd  */
1480
1481   SVN_ERR(svn_config_get_bool(db->config, &sqlite_exclusive,
1482                               SVN_CONFIG_SECTION_WORKING_COPY,
1483                               SVN_CONFIG_OPTION_SQLITE_EXCLUSIVE,
1484                               FALSE));
1485
1486   /* Create the SDB and insert the basic rows.  */
1487   SVN_ERR(create_db(&sdb, &repos_id, &wc_id, local_abspath, repos_root_url,
1488                     repos_uuid, SDB_FILE,
1489                     repos_relpath, initial_rev, depth, sqlite_exclusive,
1490                     sqlite_timeout,
1491                     db->state_pool, scratch_pool));
1492
1493   /* Create the WCROOT for this directory.  */
1494   SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
1495                         apr_pstrdup(db->state_pool, local_abspath),
1496                         sdb, wc_id, FORMAT_FROM_SDB,
1497                         FALSE /* auto-upgrade */,
1498                         db->state_pool, scratch_pool));
1499
1500   /* Any previously cached children may now have a new WCROOT, most likely that
1501      of the new WCROOT, but there might be descendant directories that are their
1502      own working copy, in which case setting WCROOT to our new WCROOT might
1503      actually break things for those.
1504
1505      Clearing is the safest thing we can do in this case, as a test would lead
1506      to unnecessary probing, while the standard code probes later anyway. So we
1507      only lose a bit of memory
1508
1509      ### Perhaps we could check wcroot->abspath to detect which case we have
1510          where, but currently it is already very hard to trigger this from
1511          the short living 'svn' client. (GUI clients like TortoiseSVN are far
1512          more likely to get in these cases)
1513      */
1514   for (hi = apr_hash_first(scratch_pool, db->dir_data);
1515        hi;
1516        hi = apr_hash_next(hi))
1517     {
1518       const char *abspath = apr_hash_this_key(hi);
1519       if (svn_dirent_is_ancestor(wcroot->abspath, abspath))
1520         svn_hash_sets(db->dir_data, abspath, NULL);
1521     }
1522
1523   /* The WCROOT is complete. Stash it into DB.  */
1524   svn_hash_sets(db->dir_data, wcroot->abspath, wcroot);
1525
1526   return SVN_NO_ERROR;
1527 }
1528
1529
1530 svn_error_t *
1531 svn_wc__db_to_relpath(const char **local_relpath,
1532                       svn_wc__db_t *db,
1533                       const char *wri_abspath,
1534                       const char *local_abspath,
1535                       apr_pool_t *result_pool,
1536                       apr_pool_t *scratch_pool)
1537 {
1538   svn_wc__db_wcroot_t *wcroot;
1539   const char *relpath;
1540
1541   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1542
1543   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &relpath, db,
1544                               wri_abspath, result_pool, scratch_pool));
1545
1546   /* This function is indirectly called from the upgrade code, so we
1547      can't verify the wcroot here. Just check that it is not NULL */
1548   CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1549
1550   if (svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
1551     {
1552       *local_relpath = apr_pstrdup(result_pool,
1553                                    svn_dirent_skip_ancestor(wcroot->abspath,
1554                                                             local_abspath));
1555     }
1556   else
1557     /* Probably moving from $TMP. Should we allow this? */
1558     *local_relpath = apr_pstrdup(result_pool, local_abspath);
1559
1560   return SVN_NO_ERROR;
1561 }
1562
1563
1564 svn_error_t *
1565 svn_wc__db_from_relpath(const char **local_abspath,
1566                         svn_wc__db_t *db,
1567                         const char *wri_abspath,
1568                         const char *local_relpath,
1569                         apr_pool_t *result_pool,
1570                         apr_pool_t *scratch_pool)
1571 {
1572   svn_wc__db_wcroot_t *wcroot;
1573   const char *unused_relpath;
1574 #if 0
1575   SVN_ERR_ASSERT(svn_relpath_is_canonical(local_relpath));
1576 #endif
1577
1578   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1579                               wri_abspath, scratch_pool, scratch_pool));
1580
1581   /* This function is indirectly called from the upgrade code, so we
1582      can't verify the wcroot here. Just check that it is not NULL */
1583   CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1584
1585
1586   *local_abspath = svn_dirent_join(wcroot->abspath,
1587                                    local_relpath,
1588                                    result_pool);
1589   return SVN_NO_ERROR;
1590 }
1591
1592
1593 svn_error_t *
1594 svn_wc__db_get_wcroot(const char **wcroot_abspath,
1595                       svn_wc__db_t *db,
1596                       const char *wri_abspath,
1597                       apr_pool_t *result_pool,
1598                       apr_pool_t *scratch_pool)
1599 {
1600   svn_wc__db_wcroot_t *wcroot;
1601   const char *unused_relpath;
1602
1603   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &unused_relpath, db,
1604                               wri_abspath, scratch_pool, scratch_pool));
1605
1606   /* Can't use VERIFY_USABLE_WCROOT, as this should be usable to detect
1607      where call upgrade */
1608   CHECK_MINIMAL_WCROOT(wcroot, wri_abspath, scratch_pool);
1609
1610   *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
1611
1612   return SVN_NO_ERROR;
1613 }
1614
1615
1616 svn_error_t *
1617 svn_wc__db_base_add_directory(svn_wc__db_t *db,
1618                               const char *local_abspath,
1619                               const char *wri_abspath,
1620                               const char *repos_relpath,
1621                               const char *repos_root_url,
1622                               const char *repos_uuid,
1623                               svn_revnum_t revision,
1624                               const apr_hash_t *props,
1625                               svn_revnum_t changed_rev,
1626                               apr_time_t changed_date,
1627                               const char *changed_author,
1628                               const apr_array_header_t *children,
1629                               svn_depth_t depth,
1630                               apr_hash_t *dav_cache,
1631                               svn_boolean_t update_actual_props,
1632                               apr_hash_t *new_actual_props,
1633                               apr_array_header_t *new_iprops,
1634                               const svn_skel_t *conflict,
1635                               const svn_skel_t *work_items,
1636                               apr_pool_t *scratch_pool)
1637 {
1638   svn_wc__db_wcroot_t *wcroot;
1639   const char *local_relpath;
1640   insert_base_baton_t ibb;
1641
1642   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1643   SVN_ERR_ASSERT(repos_relpath != NULL);
1644   SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1645   SVN_ERR_ASSERT(repos_uuid != NULL);
1646   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1647   SVN_ERR_ASSERT(props != NULL);
1648   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1649 #if 0
1650   SVN_ERR_ASSERT(children != NULL);
1651 #endif
1652
1653   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1654                               wri_abspath, scratch_pool, scratch_pool));
1655   VERIFY_USABLE_WCROOT(wcroot);
1656   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1657
1658   blank_ibb(&ibb);
1659
1660   /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1661   ibb.repos_root_url = repos_root_url;
1662   ibb.repos_uuid = repos_uuid;
1663
1664   ibb.status = svn_wc__db_status_normal;
1665   ibb.kind = svn_node_dir;
1666   ibb.repos_relpath = repos_relpath;
1667   ibb.revision = revision;
1668
1669   ibb.iprops = new_iprops;
1670   ibb.props = props;
1671   ibb.changed_rev = changed_rev;
1672   ibb.changed_date = changed_date;
1673   ibb.changed_author = changed_author;
1674
1675   ibb.children = children;
1676   ibb.depth = depth;
1677
1678   ibb.dav_cache = dav_cache;
1679   ibb.conflict = conflict;
1680   ibb.work_items = work_items;
1681
1682   if (update_actual_props)
1683     {
1684       ibb.update_actual_props = TRUE;
1685       ibb.new_actual_props = new_actual_props;
1686     }
1687
1688   /* Insert the directory and all its children transactionally.
1689
1690      Note: old children can stick around, even if they are no longer present
1691      in this directory's revision.  */
1692   SVN_WC__DB_WITH_TXN(
1693             insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1694             wcroot);
1695
1696   SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
1697   return SVN_NO_ERROR;
1698 }
1699
1700 svn_error_t *
1701 svn_wc__db_base_add_incomplete_directory(svn_wc__db_t *db,
1702                                          const char *local_abspath,
1703                                          const char *repos_relpath,
1704                                          const char *repos_root_url,
1705                                          const char *repos_uuid,
1706                                          svn_revnum_t revision,
1707                                          svn_depth_t depth,
1708                                          svn_boolean_t insert_base_deleted,
1709                                          svn_boolean_t delete_working,
1710                                          svn_skel_t *conflict,
1711                                          svn_skel_t *work_items,
1712                                          apr_pool_t *scratch_pool)
1713 {
1714   svn_wc__db_wcroot_t *wcroot;
1715   const char *local_relpath;
1716   struct insert_base_baton_t ibb;
1717
1718   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1719   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1720   SVN_ERR_ASSERT(repos_relpath && repos_root_url && repos_uuid);
1721
1722   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
1723                                                 db, local_abspath,
1724                                                 scratch_pool, scratch_pool));
1725
1726   VERIFY_USABLE_WCROOT(wcroot);
1727
1728   blank_ibb(&ibb);
1729
1730   /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1731   ibb.repos_root_url = repos_root_url;
1732   ibb.repos_uuid = repos_uuid;
1733
1734   ibb.status = svn_wc__db_status_incomplete;
1735   ibb.kind = svn_node_dir;
1736   ibb.repos_relpath = repos_relpath;
1737   ibb.revision = revision;
1738   ibb.depth = depth;
1739   ibb.insert_base_deleted = insert_base_deleted;
1740   ibb.delete_working = delete_working;
1741
1742   ibb.conflict = conflict;
1743   ibb.work_items = work_items;
1744
1745   SVN_WC__DB_WITH_TXN(
1746             insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1747             wcroot);
1748
1749   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
1750
1751   return SVN_NO_ERROR;
1752 }
1753
1754
1755 svn_error_t *
1756 svn_wc__db_base_add_file(svn_wc__db_t *db,
1757                          const char *local_abspath,
1758                          const char *wri_abspath,
1759                          const char *repos_relpath,
1760                          const char *repos_root_url,
1761                          const char *repos_uuid,
1762                          svn_revnum_t revision,
1763                          const apr_hash_t *props,
1764                          svn_revnum_t changed_rev,
1765                          apr_time_t changed_date,
1766                          const char *changed_author,
1767                          const svn_checksum_t *checksum,
1768                          apr_hash_t *dav_cache,
1769                          svn_boolean_t delete_working,
1770                          svn_boolean_t update_actual_props,
1771                          apr_hash_t *new_actual_props,
1772                          apr_array_header_t *new_iprops,
1773                          svn_boolean_t keep_recorded_info,
1774                          svn_boolean_t insert_base_deleted,
1775                          const svn_skel_t *conflict,
1776                          const svn_skel_t *work_items,
1777                          apr_pool_t *scratch_pool)
1778 {
1779   svn_wc__db_wcroot_t *wcroot;
1780   const char *local_relpath;
1781   insert_base_baton_t ibb;
1782
1783   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1784   SVN_ERR_ASSERT(repos_relpath != NULL);
1785   SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1786   SVN_ERR_ASSERT(repos_uuid != NULL);
1787   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1788   SVN_ERR_ASSERT(props != NULL);
1789   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1790   SVN_ERR_ASSERT(checksum != NULL);
1791
1792   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1793                               wri_abspath, scratch_pool, scratch_pool));
1794   VERIFY_USABLE_WCROOT(wcroot);
1795   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1796
1797   blank_ibb(&ibb);
1798
1799   /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1800   ibb.repos_root_url = repos_root_url;
1801   ibb.repos_uuid = repos_uuid;
1802
1803   ibb.status = svn_wc__db_status_normal;
1804   ibb.kind = svn_node_file;
1805   ibb.repos_relpath = repos_relpath;
1806   ibb.revision = revision;
1807
1808   ibb.props = props;
1809   ibb.changed_rev = changed_rev;
1810   ibb.changed_date = changed_date;
1811   ibb.changed_author = changed_author;
1812
1813   ibb.checksum = checksum;
1814
1815   ibb.dav_cache = dav_cache;
1816   ibb.iprops = new_iprops;
1817
1818   if (update_actual_props)
1819     {
1820       ibb.update_actual_props = TRUE;
1821       ibb.new_actual_props = new_actual_props;
1822     }
1823
1824   ibb.keep_recorded_info = keep_recorded_info;
1825   ibb.insert_base_deleted = insert_base_deleted;
1826   ibb.delete_working = delete_working;
1827
1828   ibb.conflict = conflict;
1829   ibb.work_items = work_items;
1830
1831   SVN_WC__DB_WITH_TXN(
1832             insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1833             wcroot);
1834
1835   /* If this used to be a directory we should remove children so pass
1836    * depth infinity. */
1837   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1838                         scratch_pool));
1839   return SVN_NO_ERROR;
1840 }
1841
1842
1843 svn_error_t *
1844 svn_wc__db_base_add_symlink(svn_wc__db_t *db,
1845                             const char *local_abspath,
1846                             const char *wri_abspath,
1847                             const char *repos_relpath,
1848                             const char *repos_root_url,
1849                             const char *repos_uuid,
1850                             svn_revnum_t revision,
1851                             const apr_hash_t *props,
1852                             svn_revnum_t changed_rev,
1853                             apr_time_t changed_date,
1854                             const char *changed_author,
1855                             const char *target,
1856                             apr_hash_t *dav_cache,
1857                             svn_boolean_t delete_working,
1858                             svn_boolean_t update_actual_props,
1859                             apr_hash_t *new_actual_props,
1860                             apr_array_header_t *new_iprops,
1861                             svn_boolean_t keep_recorded_info,
1862                             svn_boolean_t insert_base_deleted,
1863                             const svn_skel_t *conflict,
1864                             const svn_skel_t *work_items,
1865                             apr_pool_t *scratch_pool)
1866 {
1867   svn_wc__db_wcroot_t *wcroot;
1868   const char *local_relpath;
1869   insert_base_baton_t ibb;
1870
1871   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1872   SVN_ERR_ASSERT(repos_relpath != NULL);
1873   SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1874   SVN_ERR_ASSERT(repos_uuid != NULL);
1875   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1876   SVN_ERR_ASSERT(props != NULL);
1877   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(changed_rev));
1878   SVN_ERR_ASSERT(target != NULL);
1879
1880   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1881                               wri_abspath, scratch_pool, scratch_pool));
1882   VERIFY_USABLE_WCROOT(wcroot);
1883   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
1884   blank_ibb(&ibb);
1885
1886   /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1887   ibb.repos_root_url = repos_root_url;
1888   ibb.repos_uuid = repos_uuid;
1889
1890   ibb.status = svn_wc__db_status_normal;
1891   ibb.kind = svn_node_symlink;
1892   ibb.repos_relpath = repos_relpath;
1893   ibb.revision = revision;
1894
1895   ibb.props = props;
1896   ibb.changed_rev = changed_rev;
1897   ibb.changed_date = changed_date;
1898   ibb.changed_author = changed_author;
1899
1900   ibb.target = target;
1901
1902   ibb.dav_cache = dav_cache;
1903   ibb.iprops = new_iprops;
1904
1905   if (update_actual_props)
1906     {
1907       ibb.update_actual_props = TRUE;
1908       ibb.new_actual_props = new_actual_props;
1909     }
1910
1911   ibb.keep_recorded_info = keep_recorded_info;
1912   ibb.insert_base_deleted = insert_base_deleted;
1913   ibb.delete_working = delete_working;
1914
1915   ibb.conflict = conflict;
1916   ibb.work_items = work_items;
1917
1918   SVN_WC__DB_WITH_TXN(
1919             insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1920             wcroot);
1921
1922   /* If this used to be a directory we should remove children so pass
1923    * depth infinity. */
1924   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1925                         scratch_pool));
1926   return SVN_NO_ERROR;
1927 }
1928
1929
1930 static svn_error_t *
1931 add_excluded_or_not_present_node(svn_wc__db_t *db,
1932                                  const char *local_abspath,
1933                                  const char *repos_relpath,
1934                                  const char *repos_root_url,
1935                                  const char *repos_uuid,
1936                                  svn_revnum_t revision,
1937                                  svn_node_kind_t kind,
1938                                  svn_wc__db_status_t status,
1939                                  const svn_skel_t *conflict,
1940                                  const svn_skel_t *work_items,
1941                                  apr_pool_t *scratch_pool)
1942 {
1943   svn_wc__db_wcroot_t *wcroot;
1944   const char *local_relpath;
1945   insert_base_baton_t ibb;
1946   const char *dir_abspath, *name;
1947
1948   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
1949   SVN_ERR_ASSERT(repos_relpath != NULL);
1950   SVN_ERR_ASSERT(svn_uri_is_canonical(repos_root_url, scratch_pool));
1951   SVN_ERR_ASSERT(repos_uuid != NULL);
1952   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(revision));
1953   SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
1954                  || status == svn_wc__db_status_excluded
1955                  || status == svn_wc__db_status_not_present);
1956
1957   /* These absent presence nodes are only useful below a parent node that is
1958      present. To avoid problems with working copies obstructing the child
1959      we calculate the wcroot and local_relpath of the parent and then add
1960      our own relpath. */
1961
1962   svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
1963
1964   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
1965                               dir_abspath, scratch_pool, scratch_pool));
1966   VERIFY_USABLE_WCROOT(wcroot);
1967
1968   local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
1969
1970   blank_ibb(&ibb);
1971
1972   /* Calculate repos_id in insert_base_node() to avoid extra transaction */
1973   ibb.repos_root_url = repos_root_url;
1974   ibb.repos_uuid = repos_uuid;
1975
1976   ibb.status = status;
1977   ibb.kind = kind;
1978   ibb.repos_relpath = repos_relpath;
1979   ibb.revision = revision;
1980
1981   /* Depending upon KIND, any of these might get used. */
1982   ibb.children = NULL;
1983   ibb.depth = svn_depth_unknown;
1984   ibb.checksum = NULL;
1985   ibb.target = NULL;
1986
1987   ibb.conflict = conflict;
1988   ibb.work_items = work_items;
1989
1990   SVN_WC__DB_WITH_TXN(
1991             insert_base_node(&ibb, wcroot, local_relpath, scratch_pool),
1992             wcroot);
1993
1994   /* If this used to be a directory we should remove children so pass
1995    * depth infinity. */
1996   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
1997                         scratch_pool));
1998
1999   return SVN_NO_ERROR;
2000 }
2001
2002
2003 svn_error_t *
2004 svn_wc__db_base_add_excluded_node(svn_wc__db_t *db,
2005                                   const char *local_abspath,
2006                                   const char *repos_relpath,
2007                                   const char *repos_root_url,
2008                                   const char *repos_uuid,
2009                                   svn_revnum_t revision,
2010                                   svn_node_kind_t kind,
2011                                   svn_wc__db_status_t status,
2012                                   const svn_skel_t *conflict,
2013                                   const svn_skel_t *work_items,
2014                                   apr_pool_t *scratch_pool)
2015 {
2016   SVN_ERR_ASSERT(status == svn_wc__db_status_server_excluded
2017                  || status == svn_wc__db_status_excluded);
2018
2019   return add_excluded_or_not_present_node(
2020     db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2021     kind, status, conflict, work_items, scratch_pool);
2022 }
2023
2024
2025 svn_error_t *
2026 svn_wc__db_base_add_not_present_node(svn_wc__db_t *db,
2027                                      const char *local_abspath,
2028                                      const char *repos_relpath,
2029                                      const char *repos_root_url,
2030                                      const char *repos_uuid,
2031                                      svn_revnum_t revision,
2032                                      svn_node_kind_t kind,
2033                                      const svn_skel_t *conflict,
2034                                      const svn_skel_t *work_items,
2035                                      apr_pool_t *scratch_pool)
2036 {
2037   return add_excluded_or_not_present_node(
2038     db, local_abspath, repos_relpath, repos_root_url, repos_uuid, revision,
2039     kind, svn_wc__db_status_not_present, conflict, work_items, scratch_pool);
2040 }
2041
2042 /* Recursively clear moved-here information at the copy-half of the move
2043  * which moved a node to MOVED_TO_RELPATH. This transforms this side of the
2044  * move into a simple copy.
2045  */
2046 static svn_error_t *
2047 clear_moved_here(svn_wc__db_wcroot_t *wcroot,
2048                  const char *moved_to_relpath,
2049                  apr_pool_t *scratch_pool)
2050 {
2051   svn_sqlite__stmt_t *stmt;
2052   int affected_rows;
2053
2054   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2055                                     STMT_CLEAR_MOVED_HERE_RECURSIVE));
2056   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, moved_to_relpath,
2057                             relpath_depth(moved_to_relpath)));
2058
2059   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2060
2061   if (affected_rows == 0)
2062      return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2063                               _("The node '%s' was not found."),
2064                               path_for_error_message(wcroot, moved_to_relpath,
2065                                                      scratch_pool));
2066
2067   return SVN_NO_ERROR;
2068 }
2069
2070 svn_error_t *
2071 svn_wc__db_op_break_move_internal(svn_wc__db_wcroot_t *wcroot,
2072                                   const char *src_relpath,
2073                                   int delete_op_depth,
2074                                   const char *dst_relpath,
2075                                   const svn_skel_t *work_items,
2076                                   apr_pool_t *scratch_pool)
2077 {
2078   svn_sqlite__stmt_t *stmt;
2079   int affected;
2080
2081   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2082                                     STMT_CLEAR_MOVED_TO_RELPATH));
2083   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, src_relpath,
2084                             delete_op_depth));
2085   SVN_ERR(svn_sqlite__update(&affected, stmt));
2086
2087   if (affected != 1)
2088     return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2089                              _("Path '%s' is not moved"),
2090                              path_for_error_message(wcroot, src_relpath,
2091                                                     scratch_pool));
2092
2093   SVN_ERR(clear_moved_here(wcroot, dst_relpath, scratch_pool));
2094
2095   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2096   return SVN_NO_ERROR;
2097 }
2098
2099
2100 /* The body of svn_wc__db_base_remove().
2101  */
2102 static svn_error_t *
2103 db_base_remove(svn_wc__db_wcroot_t *wcroot,
2104                const char *local_relpath,
2105                svn_wc__db_t *db, /* For checking conflicts */
2106                svn_boolean_t keep_as_working,
2107                svn_boolean_t mark_not_present,
2108                svn_boolean_t mark_excluded,
2109                svn_revnum_t marker_revision,
2110                svn_skel_t *conflict,
2111                svn_skel_t *work_items,
2112                apr_pool_t *scratch_pool)
2113 {
2114   svn_sqlite__stmt_t *stmt;
2115   svn_boolean_t have_row;
2116   svn_wc__db_status_t status;
2117   svn_revnum_t revision;
2118   apr_int64_t repos_id;
2119   const char *repos_relpath;
2120   svn_node_kind_t kind;
2121   svn_boolean_t keep_working;
2122   int op_depth;
2123   svn_node_kind_t wrk_kind;
2124   svn_boolean_t no_delete_wc = FALSE;
2125   svn_boolean_t file_external;
2126
2127   SVN_ERR(svn_wc__db_base_get_info_internal(&status, &kind, &revision,
2128                                             &repos_relpath, &repos_id,
2129                                             NULL, NULL, NULL, NULL, NULL,
2130                                             NULL, NULL, NULL, NULL,
2131                                             &file_external,
2132                                             wcroot, local_relpath,
2133                                             scratch_pool, scratch_pool));
2134
2135   /* Check if there is already a working node */
2136   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2137                                     STMT_SELECT_NODE_INFO));
2138   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2139   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2140
2141   if (!have_row)
2142     return svn_error_trace(svn_sqlite__reset(stmt)); /* No BASE */
2143
2144   op_depth = svn_sqlite__column_int(stmt, 0);
2145   wrk_kind = svn_sqlite__column_token(stmt, 4, kind_map);
2146
2147   if (op_depth > 0
2148       && op_depth == relpath_depth(local_relpath))
2149     {
2150       svn_wc__db_status_t presence;
2151       presence = svn_sqlite__column_token(stmt, 3, presence_map);
2152
2153       if (presence == svn_wc__db_status_base_deleted)
2154         {
2155           keep_working = FALSE;
2156           no_delete_wc = TRUE;
2157         }
2158       else
2159         {
2160           keep_working = TRUE;
2161         }
2162     }
2163   else
2164     keep_working = FALSE;
2165   SVN_ERR(svn_sqlite__reset(stmt));
2166
2167   if (keep_as_working && op_depth == 0)
2168     {
2169       if (status == svn_wc__db_status_normal
2170           || status == svn_wc__db_status_incomplete)
2171         {
2172           SVN_ERR(svn_wc__db_op_make_copy_internal(wcroot, local_relpath, TRUE,
2173                                                    NULL, NULL,
2174                                                    scratch_pool));
2175         }
2176       keep_working = TRUE;
2177     }
2178
2179   /* Step 1: Create workqueue operations to remove files and dirs in the
2180      local-wc */
2181   if (!keep_working && !no_delete_wc)
2182     {
2183       svn_skel_t *work_item;
2184       const char *local_abspath;
2185
2186       local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
2187                                       scratch_pool);
2188       if (wrk_kind == svn_node_dir)
2189         {
2190           apr_pool_t *iterpool;
2191           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2192                                             STMT_SELECT_WORKING_PRESENT));
2193           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2194
2195           iterpool = svn_pool_create(scratch_pool);
2196
2197           SVN_ERR(svn_sqlite__step(&have_row, stmt));
2198
2199           while (have_row)
2200             {
2201               const char *node_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2202               svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 1,
2203                                                               kind_map);
2204               const char *node_abspath;
2205               svn_error_t *err;
2206
2207               svn_pool_clear(iterpool);
2208
2209               node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
2210                                              iterpool);
2211
2212               if (node_kind == svn_node_dir)
2213                 err = svn_wc__wq_build_dir_remove(&work_item,
2214                                                   db, wcroot->abspath,
2215                                                   node_abspath, FALSE,
2216                                                   iterpool, iterpool);
2217               else
2218                 err = svn_wc__wq_build_file_remove(&work_item,
2219                                                    db,
2220                                                    wcroot->abspath,
2221                                                    node_abspath,
2222                                                    iterpool, iterpool);
2223
2224               if (!err)
2225                 err = add_work_items(wcroot->sdb, work_item, iterpool);
2226               if (err)
2227                 return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2228
2229               SVN_ERR(svn_sqlite__step(&have_row, stmt));
2230            }
2231
2232           SVN_ERR(svn_sqlite__reset(stmt));
2233
2234           SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
2235                                               db, wcroot->abspath,
2236                                               local_abspath, FALSE,
2237                                               scratch_pool, iterpool));
2238           svn_pool_destroy(iterpool);
2239         }
2240       else
2241         SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
2242                                              db, wcroot->abspath,
2243                                              local_abspath,
2244                                              scratch_pool, scratch_pool));
2245
2246       SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
2247     }
2248
2249   /* Step 2: Delete ACTUAL nodes */
2250   if (! keep_working)
2251     {
2252       /* There won't be a record in NODE left for this node, so we want
2253          to remove *all* ACTUAL nodes, including ACTUAL ONLY. */
2254       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2255                                         STMT_DELETE_ACTUAL_NODE_RECURSIVE));
2256       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2257       SVN_ERR(svn_sqlite__step_done(stmt));
2258     }
2259   else if (! keep_as_working)
2260     {
2261       /* Delete only the ACTUAL nodes that apply to a delete of a BASE node */
2262       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2263                                        STMT_DELETE_ACTUAL_FOR_BASE_RECURSIVE));
2264       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2265       SVN_ERR(svn_sqlite__step_done(stmt));
2266     }
2267   /* Else: Everything has been turned into a copy, so we want to keep all
2268            ACTUAL_NODE records */
2269
2270   /* Step 3: Delete WORKING nodes */
2271   if (!keep_working)
2272     {
2273       apr_pool_t *iterpool;
2274
2275       /* When deleting everything in working we should break moves from
2276          here and to here.
2277        */
2278       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2279                                         STMT_SELECT_MOVED_OUTSIDE));
2280       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2281                                              local_relpath,
2282                                              relpath_depth(local_relpath)));
2283       SVN_ERR(svn_sqlite__step(&have_row, stmt));
2284       iterpool = svn_pool_create(scratch_pool);
2285       while (have_row)
2286         {
2287           const char *moved_to_relpath;
2288           svn_error_t *err;
2289
2290           svn_pool_clear(iterpool);
2291           moved_to_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
2292           err = clear_moved_here(wcroot, moved_to_relpath, iterpool);
2293           if (err)
2294             return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2295           SVN_ERR(svn_sqlite__step(&have_row, stmt));
2296         }
2297       svn_pool_destroy(iterpool);
2298       SVN_ERR(svn_sqlite__reset(stmt));
2299     }
2300   else
2301     {
2302       /* We are keeping things that are in WORKING, but we should still
2303          break moves of things in BASE. (Mixed revisions make it
2304          impossible to guarantee that we can keep everything moved) */
2305
2306       apr_pool_t *iterpool;
2307
2308       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2309                                         STMT_SELECT_MOVED_DESCENDANTS_SRC));
2310       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
2311                                 local_relpath, 0));
2312       SVN_ERR(svn_sqlite__step(&have_row, stmt));
2313       iterpool = svn_pool_create(scratch_pool);
2314       while (have_row)
2315         {
2316           int delete_op_depth = svn_sqlite__column_int(stmt, 0);
2317           const char *src_relpath;
2318           const char *dst_relpath;
2319           svn_error_t *err;
2320
2321           svn_pool_clear(iterpool);
2322
2323           src_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
2324           dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool);
2325
2326           err = svn_wc__db_op_break_move_internal(wcroot, src_relpath,
2327                                                   delete_op_depth,
2328                                                   dst_relpath,
2329                                                   NULL,
2330                                                   iterpool);
2331
2332           if (err)
2333             return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2334
2335           SVN_ERR(svn_sqlite__step(&have_row, stmt));
2336         }
2337       svn_pool_destroy(iterpool);
2338       SVN_ERR(svn_sqlite__reset(stmt));
2339     }
2340   if (keep_working)
2341     {
2342       SVN_ERR(svn_sqlite__get_statement(
2343                     &stmt, wcroot->sdb,
2344                     STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE));
2345       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath, 0));
2346       SVN_ERR(svn_sqlite__step_done(stmt));
2347     }
2348   else
2349     {
2350       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2351                                         STMT_DELETE_WORKING_RECURSIVE));
2352       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2353       SVN_ERR(svn_sqlite__step_done(stmt));
2354     }
2355
2356   /* Step 4: Delete the BASE node descendants */
2357   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2358                                     STMT_DELETE_BASE_RECURSIVE));
2359   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2360   SVN_ERR(svn_sqlite__step_done(stmt));
2361
2362   SVN_ERR(db_retract_parent_delete(wcroot, local_relpath, 0, scratch_pool));
2363
2364   if (mark_not_present || mark_excluded)
2365     {
2366       struct insert_base_baton_t ibb;
2367       svn_boolean_t no_marker = FALSE;
2368
2369       if (file_external)
2370         {
2371           const char *parent_local_relpath;
2372           const char *name;
2373           svn_error_t *err;
2374
2375           /* For file externals we only want to place a not present marker
2376              if there is a BASE parent */
2377           
2378           svn_relpath_split(&parent_local_relpath, &name, local_relpath,
2379                             scratch_pool);
2380
2381           err = svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
2382                                                   &repos_relpath, &repos_id,
2383                                                   NULL, NULL, NULL, NULL, NULL,
2384                                                   NULL, NULL, NULL, NULL, NULL,
2385                                                   wcroot, parent_local_relpath,
2386                                                   scratch_pool, scratch_pool);
2387
2388           if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
2389             return svn_error_trace(err);
2390           else if (err)
2391             {
2392               svn_error_clear(err);
2393               no_marker = TRUE;
2394             }
2395           else
2396             {
2397               /* Replace the repos_relpath with something more expected than
2398                  the unrelated old file external repository relpath, which
2399                  one day may come from a different repository */
2400               repos_relpath = svn_relpath_join(repos_relpath, name, scratch_pool);
2401             }
2402         }
2403
2404       if (!no_marker)
2405         {
2406           blank_ibb(&ibb);
2407
2408           ibb.repos_id = repos_id;
2409           ibb.status = mark_excluded ? svn_wc__db_status_excluded
2410                                      : svn_wc__db_status_not_present;
2411           ibb.kind = kind;
2412           ibb.repos_relpath = repos_relpath;
2413           ibb.revision = SVN_IS_VALID_REVNUM(marker_revision)
2414                             ? marker_revision
2415                             : revision;
2416
2417           /* Depending upon KIND, any of these might get used. */
2418           ibb.children = NULL;
2419           ibb.depth = svn_depth_unknown;
2420           ibb.checksum = NULL;
2421           ibb.target = NULL;
2422
2423           SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
2424         }
2425     }
2426
2427   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
2428   if (conflict)
2429     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
2430                                               conflict, scratch_pool));
2431
2432   return SVN_NO_ERROR;
2433 }
2434
2435
2436 svn_error_t *
2437 svn_wc__db_base_remove(svn_wc__db_t *db,
2438                        const char *local_abspath,
2439                        svn_boolean_t keep_as_working,
2440                        svn_boolean_t mark_not_present,
2441                        svn_boolean_t mark_excluded,
2442                        svn_revnum_t marker_revision,
2443                        svn_skel_t *conflict,
2444                        svn_skel_t *work_items,
2445                        apr_pool_t *scratch_pool)
2446 {
2447   svn_wc__db_wcroot_t *wcroot;
2448   const char *local_relpath;
2449
2450   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2451
2452   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2453                               local_abspath, scratch_pool, scratch_pool));
2454   VERIFY_USABLE_WCROOT(wcroot);
2455
2456   SVN_WC__DB_WITH_TXN(db_base_remove(wcroot, local_relpath,
2457                                      db, keep_as_working,
2458                                      mark_not_present, mark_excluded,
2459                                      marker_revision,
2460                                      conflict, work_items, scratch_pool),
2461                       wcroot);
2462
2463   /* If this used to be a directory we should remove children so pass
2464    * depth infinity. */
2465   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
2466                         scratch_pool));
2467
2468   return SVN_NO_ERROR;
2469 }
2470
2471
2472 svn_error_t *
2473 svn_wc__db_base_get_info_internal(svn_wc__db_status_t *status,
2474                                   svn_node_kind_t *kind,
2475                                   svn_revnum_t *revision,
2476                                   const char **repos_relpath,
2477                                   apr_int64_t *repos_id,
2478                                   svn_revnum_t *changed_rev,
2479                                   apr_time_t *changed_date,
2480                                   const char **changed_author,
2481                                   svn_depth_t *depth,
2482                                   const svn_checksum_t **checksum,
2483                                   const char **target,
2484                                   svn_wc__db_lock_t **lock,
2485                                   svn_boolean_t *had_props,
2486                                   apr_hash_t **props,
2487                                   svn_boolean_t *update_root,
2488                                   svn_wc__db_wcroot_t *wcroot,
2489                                   const char *local_relpath,
2490                                   apr_pool_t *result_pool,
2491                                   apr_pool_t *scratch_pool)
2492 {
2493   svn_sqlite__stmt_t *stmt;
2494   svn_boolean_t have_row;
2495   svn_error_t *err = SVN_NO_ERROR;
2496
2497   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2498                                     lock ? STMT_SELECT_BASE_NODE_WITH_LOCK
2499                                          : STMT_SELECT_BASE_NODE));
2500   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2501   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2502
2503   if (have_row)
2504     {
2505       svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2506                                                                  presence_map);
2507       svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2508
2509       if (kind)
2510         {
2511           *kind = node_kind;
2512         }
2513       if (status)
2514         {
2515           *status = node_status;
2516         }
2517       repos_location_from_columns(repos_id, revision, repos_relpath,
2518                                   stmt, 0, 4, 1, result_pool);
2519       SVN_ERR_ASSERT(!repos_id || *repos_id != INVALID_REPOS_ID);
2520       SVN_ERR_ASSERT(!repos_relpath || *repos_relpath);
2521       if (lock)
2522         {
2523           *lock = lock_from_columns(stmt, 15, 16, 17, 18, result_pool);
2524         }
2525       if (changed_rev)
2526         {
2527           *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2528         }
2529       if (changed_date)
2530         {
2531           *changed_date = svn_sqlite__column_int64(stmt, 8);
2532         }
2533       if (changed_author)
2534         {
2535           /* Result may be NULL. */
2536           *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2537         }
2538       if (depth)
2539         {
2540           if (node_kind != svn_node_dir)
2541             {
2542               *depth = svn_depth_unknown;
2543             }
2544           else
2545             {
2546               *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2547                                                      svn_depth_unknown);
2548             }
2549         }
2550       if (checksum)
2551         {
2552           if (node_kind != svn_node_file)
2553             {
2554               *checksum = NULL;
2555             }
2556           else
2557             {
2558               err = svn_sqlite__column_checksum(checksum, stmt, 5,
2559                                                 result_pool);
2560               if (err != NULL)
2561                 err = svn_error_createf(
2562                         err->apr_err, err,
2563                         _("The node '%s' has a corrupt checksum value."),
2564                         path_for_error_message(wcroot, local_relpath,
2565                                                scratch_pool));
2566             }
2567         }
2568       if (target)
2569         {
2570           if (node_kind != svn_node_symlink)
2571             *target = NULL;
2572           else
2573             *target = svn_sqlite__column_text(stmt, 11, result_pool);
2574         }
2575       if (had_props)
2576         {
2577           *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 13);
2578         }
2579       if (props)
2580         {
2581           if (node_status == svn_wc__db_status_normal
2582               || node_status == svn_wc__db_status_incomplete)
2583             {
2584               SVN_ERR(svn_sqlite__column_properties(props, stmt, 13,
2585                                                     result_pool, scratch_pool));
2586               if (*props == NULL)
2587                 *props = apr_hash_make(result_pool);
2588             }
2589           else
2590             {
2591               assert(svn_sqlite__column_is_null(stmt, 13));
2592               *props = NULL;
2593             }
2594         }
2595       if (update_root)
2596         {
2597           /* It's an update root iff it's a file external. */
2598           *update_root = svn_sqlite__column_boolean(stmt, 14);
2599         }
2600     }
2601   else
2602     {
2603       err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2604                               _("The node '%s' was not found."),
2605                               path_for_error_message(wcroot, local_relpath,
2606                                                      scratch_pool));
2607     }
2608
2609   /* Note: given the composition, no need to wrap for tracing.  */
2610   return svn_error_compose_create(err, svn_sqlite__reset(stmt));
2611 }
2612
2613
2614 svn_error_t *
2615 svn_wc__db_base_get_info(svn_wc__db_status_t *status,
2616                          svn_node_kind_t *kind,
2617                          svn_revnum_t *revision,
2618                          const char **repos_relpath,
2619                          const char **repos_root_url,
2620                          const char **repos_uuid,
2621                          svn_revnum_t *changed_rev,
2622                          apr_time_t *changed_date,
2623                          const char **changed_author,
2624                          svn_depth_t *depth,
2625                          const svn_checksum_t **checksum,
2626                          const char **target,
2627                          svn_wc__db_lock_t **lock,
2628                          svn_boolean_t *had_props,
2629                          apr_hash_t **props,
2630                          svn_boolean_t *update_root,
2631                          svn_wc__db_t *db,
2632                          const char *local_abspath,
2633                          apr_pool_t *result_pool,
2634                          apr_pool_t *scratch_pool)
2635 {
2636   svn_wc__db_wcroot_t *wcroot;
2637   const char *local_relpath;
2638   apr_int64_t repos_id;
2639
2640   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2641
2642   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2643                               local_abspath, scratch_pool, scratch_pool));
2644   VERIFY_USABLE_WCROOT(wcroot);
2645
2646   SVN_WC__DB_WITH_TXN4(
2647           svn_wc__db_base_get_info_internal(status, kind, revision,
2648                                             repos_relpath, &repos_id,
2649                                             changed_rev, changed_date,
2650                                             changed_author, depth,
2651                                             checksum, target, lock,
2652                                             had_props, props, update_root,
2653                                             wcroot, local_relpath,
2654                                             result_pool, scratch_pool),
2655           svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
2656                                       wcroot, repos_id, result_pool),
2657           SVN_NO_ERROR,
2658           SVN_NO_ERROR,
2659           wcroot);
2660   SVN_ERR_ASSERT(repos_id != INVALID_REPOS_ID);
2661
2662   return SVN_NO_ERROR;
2663 }
2664
2665 /* The implementation of svn_wc__db_base_get_children_info */
2666 static svn_error_t *
2667 base_get_children_info(apr_hash_t **nodes,
2668                        svn_wc__db_wcroot_t *wcroot,
2669                        const char *local_relpath,
2670                        svn_boolean_t obtain_locks,
2671                        apr_pool_t *result_pool,
2672                        apr_pool_t *scratch_pool)
2673 {
2674   svn_sqlite__stmt_t *stmt;
2675   svn_boolean_t have_row;
2676   apr_int64_t last_repos_id = INVALID_REPOS_ID;
2677   const char *last_repos_root_url = NULL;
2678
2679   *nodes = apr_hash_make(result_pool);
2680
2681   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2682                                     obtain_locks
2683                                       ? STMT_SELECT_BASE_CHILDREN_INFO_LOCK
2684                                       : STMT_SELECT_BASE_CHILDREN_INFO));
2685   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2686
2687   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2688
2689   while (have_row)
2690     {
2691       struct svn_wc__db_base_info_t *info;
2692       apr_int64_t repos_id;
2693       const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
2694       const char *name = svn_relpath_basename(child_relpath, result_pool);
2695
2696       info = apr_pcalloc(result_pool, sizeof(*info));
2697
2698       repos_id = svn_sqlite__column_int64(stmt, 1);
2699       info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
2700       info->status = svn_sqlite__column_token(stmt, 3, presence_map);
2701       info->kind = svn_sqlite__column_token(stmt, 4, kind_map);
2702       info->revnum = svn_sqlite__column_revnum(stmt, 5);
2703
2704       info->depth = svn_sqlite__column_token_null(stmt, 6, depth_map,
2705                                                   svn_depth_unknown);
2706
2707       info->update_root = svn_sqlite__column_boolean(stmt, 7);
2708
2709       if (obtain_locks)
2710         info->lock = lock_from_columns(stmt, 8, 9, 10, 11, result_pool);
2711
2712       if (repos_id != last_repos_id)
2713         {
2714           svn_error_t *err;
2715
2716           err = svn_wc__db_fetch_repos_info(&last_repos_root_url, NULL,
2717                                             wcroot, repos_id,
2718                                             result_pool);
2719
2720           if (err)
2721             return svn_error_trace(
2722                      svn_error_compose_create(err,
2723                                               svn_sqlite__reset(stmt)));
2724
2725           last_repos_id = repos_id;
2726         }
2727
2728       info->repos_root_url = last_repos_root_url;
2729
2730       svn_hash_sets(*nodes, name, info);
2731
2732       SVN_ERR(svn_sqlite__step(&have_row, stmt));
2733     }
2734
2735   SVN_ERR(svn_sqlite__reset(stmt));
2736
2737   return SVN_NO_ERROR;
2738 }
2739
2740 svn_error_t *
2741 svn_wc__db_base_get_children_info(apr_hash_t **nodes,
2742                                   svn_wc__db_t *db,
2743                                   const char *dir_abspath,
2744                                   apr_pool_t *result_pool,
2745                                   apr_pool_t *scratch_pool)
2746 {
2747   svn_wc__db_wcroot_t *wcroot;
2748   const char *local_relpath;
2749
2750   SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
2751
2752   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2753                               dir_abspath, scratch_pool, scratch_pool));
2754   VERIFY_USABLE_WCROOT(wcroot);
2755
2756   return svn_error_trace(base_get_children_info(nodes,
2757                                                 wcroot,
2758                                                 local_relpath,
2759                                                 TRUE /* obtain_locks */,
2760                                                 result_pool,
2761                                                 scratch_pool));
2762 }
2763
2764
2765 svn_error_t *
2766 svn_wc__db_base_get_props(apr_hash_t **props,
2767                           svn_wc__db_t *db,
2768                           const char *local_abspath,
2769                           apr_pool_t *result_pool,
2770                           apr_pool_t *scratch_pool)
2771 {
2772   svn_wc__db_status_t presence;
2773
2774   SVN_ERR(svn_wc__db_base_get_info(&presence, NULL, NULL, NULL, NULL,
2775                                    NULL, NULL, NULL, NULL, NULL,
2776                                    NULL, NULL, NULL, NULL, props, NULL,
2777                                    db, local_abspath,
2778                                    result_pool, scratch_pool));
2779   if (presence != svn_wc__db_status_normal
2780       && presence != svn_wc__db_status_incomplete)
2781     {
2782       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
2783                                _("The node '%s' has a BASE status that"
2784                                   " has no properties."),
2785                                svn_dirent_local_style(local_abspath,
2786                                                       scratch_pool));
2787     }
2788
2789   return SVN_NO_ERROR;
2790 }
2791
2792
2793 svn_error_t *
2794 svn_wc__db_base_get_children(const apr_array_header_t **children,
2795                              svn_wc__db_t *db,
2796                              const char *local_abspath,
2797                              apr_pool_t *result_pool,
2798                              apr_pool_t *scratch_pool)
2799 {
2800   svn_wc__db_wcroot_t *wcroot;
2801   const char *local_relpath;
2802
2803   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2804
2805   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2806                                              local_abspath,
2807                                              scratch_pool, scratch_pool));
2808   VERIFY_USABLE_WCROOT(wcroot);
2809
2810   return svn_error_trace(
2811               gather_children(children, wcroot, local_relpath,
2812                               STMT_SELECT_OP_DEPTH_CHILDREN, 0,
2813                               result_pool, scratch_pool));
2814 }
2815
2816
2817 svn_error_t *
2818 svn_wc__db_base_set_dav_cache(svn_wc__db_t *db,
2819                               const char *local_abspath,
2820                               const apr_hash_t *props,
2821                               apr_pool_t *scratch_pool)
2822 {
2823   svn_wc__db_wcroot_t *wcroot;
2824   const char *local_relpath;
2825   svn_sqlite__stmt_t *stmt;
2826   int affected_rows;
2827
2828   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2829
2830   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2831                               local_abspath, scratch_pool, scratch_pool));
2832   VERIFY_USABLE_WCROOT(wcroot);
2833
2834   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2835                                     STMT_UPDATE_BASE_NODE_DAV_CACHE));
2836   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2837   SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
2838
2839   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
2840
2841   if (affected_rows != 1)
2842     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
2843                              _("The node '%s' was not found."),
2844                              svn_dirent_local_style(local_abspath,
2845                                                     scratch_pool));
2846
2847   return SVN_NO_ERROR;
2848 }
2849
2850
2851 svn_error_t *
2852 svn_wc__db_base_get_dav_cache(apr_hash_t **props,
2853                               svn_wc__db_t *db,
2854                               const char *local_abspath,
2855                               apr_pool_t *result_pool,
2856                               apr_pool_t *scratch_pool)
2857 {
2858   svn_wc__db_wcroot_t *wcroot;
2859   const char *local_relpath;
2860   svn_sqlite__stmt_t *stmt;
2861   svn_boolean_t have_row;
2862
2863   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
2864
2865   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
2866                               local_abspath, scratch_pool, scratch_pool));
2867   VERIFY_USABLE_WCROOT(wcroot);
2868
2869   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2870                                     STMT_SELECT_BASE_DAV_CACHE));
2871
2872   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2873   if (!have_row)
2874     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
2875                              svn_sqlite__reset(stmt),
2876                              _("The node '%s' was not found."),
2877                              svn_dirent_local_style(local_abspath,
2878                                                     scratch_pool));
2879
2880   SVN_ERR(svn_sqlite__column_properties(props, stmt, 0, result_pool,
2881                                         scratch_pool));
2882   return svn_error_trace(svn_sqlite__reset(stmt));
2883 }
2884
2885
2886 svn_error_t *
2887 svn_wc__db_base_clear_dav_cache_recursive(svn_wc__db_t *db,
2888                                           const char *local_abspath,
2889                                           apr_pool_t *scratch_pool)
2890 {
2891   svn_wc__db_wcroot_t *wcroot;
2892   const char *local_relpath;
2893   svn_sqlite__stmt_t *stmt;
2894
2895   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
2896                                              db, local_abspath,
2897                                              scratch_pool, scratch_pool));
2898   VERIFY_USABLE_WCROOT(wcroot);
2899
2900   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2901                                     STMT_CLEAR_BASE_NODE_RECURSIVE_DAV_CACHE));
2902   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
2903
2904   SVN_ERR(svn_sqlite__step_done(stmt));
2905
2906   return SVN_NO_ERROR;
2907 }
2908
2909
2910 svn_error_t *
2911 svn_wc__db_depth_get_info(svn_wc__db_status_t *status,
2912                           svn_node_kind_t *kind,
2913                           svn_revnum_t *revision,
2914                           const char **repos_relpath,
2915                           apr_int64_t *repos_id,
2916                           svn_revnum_t *changed_rev,
2917                           apr_time_t *changed_date,
2918                           const char **changed_author,
2919                           svn_depth_t *depth,
2920                           const svn_checksum_t **checksum,
2921                           const char **target,
2922                           svn_boolean_t *had_props,
2923                           apr_hash_t **props,
2924                           svn_wc__db_wcroot_t *wcroot,
2925                           const char *local_relpath,
2926                           int op_depth,
2927                           apr_pool_t *result_pool,
2928                           apr_pool_t *scratch_pool)
2929 {
2930   svn_sqlite__stmt_t *stmt;
2931   svn_boolean_t have_row;
2932   svn_error_t *err = SVN_NO_ERROR;
2933
2934   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
2935                                     STMT_SELECT_DEPTH_NODE));
2936   SVN_ERR(svn_sqlite__bindf(stmt, "isd",
2937                             wcroot->wc_id, local_relpath, op_depth));
2938   SVN_ERR(svn_sqlite__step(&have_row, stmt));
2939
2940   if (have_row)
2941     {
2942       svn_wc__db_status_t node_status = svn_sqlite__column_token(stmt, 2,
2943                                                                  presence_map);
2944       svn_node_kind_t node_kind = svn_sqlite__column_token(stmt, 3, kind_map);
2945
2946       if (kind)
2947         {
2948           *kind = node_kind;
2949         }
2950       if (status)
2951         {
2952           *status = node_status;
2953
2954           if (op_depth > 0)
2955             SVN_ERR(convert_to_working_status(status, *status));
2956         }
2957       repos_location_from_columns(repos_id, revision, repos_relpath,
2958                                   stmt, 0, 4, 1, result_pool);
2959
2960       if (changed_rev)
2961         {
2962           *changed_rev = svn_sqlite__column_revnum(stmt, 7);
2963         }
2964       if (changed_date)
2965         {
2966           *changed_date = svn_sqlite__column_int64(stmt, 8);
2967         }
2968       if (changed_author)
2969         {
2970           /* Result may be NULL. */
2971           *changed_author = svn_sqlite__column_text(stmt, 9, result_pool);
2972         }
2973       if (depth)
2974         {
2975           if (node_kind != svn_node_dir)
2976             {
2977               *depth = svn_depth_unknown;
2978             }
2979           else
2980             {
2981               *depth = svn_sqlite__column_token_null(stmt, 10, depth_map,
2982                                                      svn_depth_unknown);
2983             }
2984         }
2985       if (checksum)
2986         {
2987           if (node_kind != svn_node_file)
2988             {
2989               *checksum = NULL;
2990             }
2991           else
2992             {
2993               err = svn_sqlite__column_checksum(checksum, stmt, 5,
2994                                                 result_pool);
2995               if (err != NULL)
2996                 err = svn_error_createf(
2997                         err->apr_err, err,
2998                         _("The node '%s' has a corrupt checksum value."),
2999                         path_for_error_message(wcroot, local_relpath,
3000                                                scratch_pool));
3001             }
3002         }
3003       if (target)
3004         {
3005           if (node_kind != svn_node_symlink)
3006             *target = NULL;
3007           else
3008             *target = svn_sqlite__column_text(stmt, 11, result_pool);
3009         }
3010       if (had_props)
3011         {
3012           *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 12);
3013         }
3014       if (props)
3015         {
3016           if (node_status == svn_wc__db_status_normal
3017               || node_status == svn_wc__db_status_incomplete)
3018             {
3019               SVN_ERR(svn_sqlite__column_properties(props, stmt, 12,
3020                                                     result_pool, scratch_pool));
3021               if (*props == NULL)
3022                 *props = apr_hash_make(result_pool);
3023             }
3024           else
3025             {
3026               assert(svn_sqlite__column_is_null(stmt, 12));
3027               *props = NULL;
3028             }
3029         }
3030     }
3031   else
3032     {
3033       err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3034                               _("The node '%s' was not found."),
3035                               path_for_error_message(wcroot, local_relpath,
3036                                                      scratch_pool));
3037     }
3038
3039   /* Note: given the composition, no need to wrap for tracing.  */
3040   return svn_error_compose_create(err, svn_sqlite__reset(stmt));
3041 }
3042
3043 /* A callback which supplies WCROOTs and LOCAL_RELPATHs. */
3044 typedef svn_error_t *(*svn_wc__db_txn_callback_t)(void *baton,
3045                                           svn_wc__db_wcroot_t *wcroot,
3046                                           const char *local_relpath,
3047                                           apr_pool_t *scratch_pool);
3048
3049 /* Baton for passing args to with_triggers(). */
3050 struct with_triggers_baton_t {
3051   int create_trigger;
3052   int drop_trigger;
3053   svn_wc__db_txn_callback_t cb_func;
3054   void *cb_baton;
3055 };
3056
3057 /* Helper for creating SQLite triggers, running the main transaction
3058    callback, and then dropping the triggers.  It guarantees that the
3059    triggers will not survive the transaction.  This could be used for
3060    any general prefix/postscript statements where the postscript
3061    *must* be executed if the transaction completes.
3062
3063    Implements svn_wc__db_txn_callback_t. */
3064 static svn_error_t *
3065 with_triggers(void *baton,
3066               svn_wc__db_wcroot_t *wcroot,
3067               const char *local_relpath,
3068               apr_pool_t *scratch_pool)
3069 {
3070   struct with_triggers_baton_t *b = baton;
3071   svn_error_t *err1;
3072   svn_error_t *err2;
3073
3074   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, b->create_trigger));
3075
3076   err1 = b->cb_func(b->cb_baton, wcroot, local_relpath, scratch_pool);
3077
3078   err2 = svn_sqlite__exec_statements(wcroot->sdb, b->drop_trigger);
3079
3080   return svn_error_trace(svn_error_compose_create(err1, err2));
3081 }
3082
3083
3084 /* Prototype for the "work callback" used by with_finalization().  */
3085 typedef svn_error_t * (*work_callback_t)(
3086                           void *baton,
3087                           svn_wc__db_wcroot_t *wcroot,
3088                           svn_cancel_func_t cancel_func,
3089                           void *cancel_baton,
3090                           svn_wc_notify_func2_t notify_func,
3091                           void *notify_baton,
3092                           apr_pool_t *scratch_pool);
3093
3094 /* Utility function to provide several features, with a guaranteed
3095    finalization (ie. to drop temporary tables).
3096
3097    1) for WCROOT and LOCAL_RELPATH, run TXN_CB(TXN_BATON) within a
3098       sqlite transaction
3099    2) if (1) is successful and a NOTIFY_FUNC is provided, then run
3100       the "work" step: WORK_CB(WORK_BATON).
3101    3) execute FINALIZE_STMT_IDX no matter what errors may be thrown
3102       from the above two steps.
3103
3104    CANCEL_FUNC, CANCEL_BATON, NOTIFY_FUNC and NOTIFY_BATON are their
3105    typical values. These are passed to the work callback, which typically
3106    provides notification about the work done by TXN_CB.  */
3107 static svn_error_t *
3108 with_finalization(svn_wc__db_wcroot_t *wcroot,
3109                   const char *local_relpath,
3110                   svn_wc__db_txn_callback_t txn_cb,
3111                   void *txn_baton,
3112                   work_callback_t work_cb,
3113                   void *work_baton,
3114                   svn_cancel_func_t cancel_func,
3115                   void *cancel_baton,
3116                   svn_wc_notify_func2_t notify_func,
3117                   void *notify_baton,
3118                   int finalize_stmt_idx,
3119                   apr_pool_t *scratch_pool)
3120 {
3121   svn_error_t *err1;
3122   svn_error_t *err2;
3123
3124   err1 = svn_sqlite__begin_savepoint(wcroot->sdb);
3125   if (!err1)
3126     {
3127       err1 = txn_cb(txn_baton, wcroot, local_relpath, scratch_pool);
3128
3129       err1 = svn_sqlite__finish_savepoint(wcroot->sdb, err1);
3130     }
3131
3132   if (err1 == NULL && notify_func != NULL)
3133     {
3134       err2 = work_cb(work_baton, wcroot,
3135                      cancel_func, cancel_baton,
3136                      notify_func, notify_baton,
3137                      scratch_pool);
3138       err1 = svn_error_compose_create(err1, err2);
3139     }
3140
3141   err2 = svn_sqlite__exec_statements(wcroot->sdb, finalize_stmt_idx);
3142
3143   return svn_error_trace(svn_error_compose_create(err1, err2));
3144 }
3145
3146
3147 /* Initialize the baton with appropriate "blank" values. This allows the
3148    insertion function to leave certain columns null.  */
3149 static void
3150 blank_ieb(insert_external_baton_t *ieb)
3151 {
3152   memset(ieb, 0, sizeof(*ieb));
3153   ieb->revision = SVN_INVALID_REVNUM;
3154   ieb->changed_rev = SVN_INVALID_REVNUM;
3155   ieb->repos_id = INVALID_REPOS_ID;
3156
3157   ieb->recorded_peg_revision = SVN_INVALID_REVNUM;
3158   ieb->recorded_revision = SVN_INVALID_REVNUM;
3159 }
3160
3161 /* Insert the externals row represented by (insert_external_baton_t *) BATON.
3162  *
3163  * Implements svn_wc__db_txn_callback_t. */
3164 static svn_error_t *
3165 insert_external_node(const insert_external_baton_t *ieb,
3166                      svn_wc__db_wcroot_t *wcroot,
3167                      const char *local_relpath,
3168                      apr_pool_t *scratch_pool)
3169 {
3170   svn_wc__db_status_t status;
3171   svn_error_t *err;
3172   svn_boolean_t update_root;
3173   apr_int64_t repos_id;
3174   svn_sqlite__stmt_t *stmt;
3175
3176   if (ieb->repos_id != INVALID_REPOS_ID)
3177     repos_id = ieb->repos_id;
3178   else
3179     SVN_ERR(create_repos_id(&repos_id, ieb->repos_root_url, ieb->repos_uuid,
3180                             wcroot->sdb, scratch_pool));
3181
3182   /* And there must be no existing BASE node or it must be a file external */
3183   err = svn_wc__db_base_get_info_internal(&status, NULL, NULL, NULL, NULL,
3184                                           NULL, NULL, NULL, NULL, NULL,
3185                                           NULL, NULL, NULL, NULL, &update_root,
3186                                           wcroot, local_relpath,
3187                                           scratch_pool, scratch_pool);
3188   if (err)
3189     {
3190       if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
3191         return svn_error_trace(err);
3192
3193       svn_error_clear(err);
3194     }
3195   else if (status == svn_wc__db_status_normal && !update_root)
3196     return svn_error_create(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL, NULL);
3197
3198   if (ieb->kind == svn_node_file
3199       || ieb->kind == svn_node_symlink)
3200     {
3201       struct insert_base_baton_t ibb;
3202
3203       blank_ibb(&ibb);
3204
3205       ibb.status          = svn_wc__db_status_normal;
3206       ibb.kind            = ieb->kind;
3207
3208       ibb.repos_id        = repos_id;
3209       ibb.repos_relpath   = ieb->repos_relpath;
3210       ibb.revision        = ieb->revision;
3211
3212       ibb.props           = ieb->props;
3213       ibb.iprops          = ieb->iprops;
3214       ibb.changed_rev     = ieb->changed_rev;
3215       ibb.changed_date    = ieb->changed_date;
3216       ibb.changed_author  = ieb->changed_author;
3217
3218       ibb.dav_cache       = ieb->dav_cache;
3219
3220       ibb.checksum        = ieb->checksum;
3221       ibb.target          = ieb->target;
3222
3223       ibb.conflict        = ieb->conflict;
3224
3225       ibb.update_actual_props = ieb->update_actual_props;
3226       ibb.new_actual_props    = ieb->new_actual_props;
3227
3228       ibb.keep_recorded_info  = ieb->keep_recorded_info;
3229
3230       ibb.work_items      = ieb->work_items;
3231
3232       ibb.file_external = TRUE;
3233
3234       SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
3235     }
3236   else
3237     SVN_ERR(add_work_items(wcroot->sdb, ieb->work_items, scratch_pool));
3238
3239   /* The externals table only support presence normal and excluded */
3240   SVN_ERR_ASSERT(ieb->presence == svn_wc__db_status_normal
3241                  || ieb->presence == svn_wc__db_status_excluded);
3242
3243   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_EXTERNAL));
3244
3245   SVN_ERR(svn_sqlite__bindf(stmt, "issttsis",
3246                             wcroot->wc_id,
3247                             local_relpath,
3248                             svn_relpath_dirname(local_relpath,
3249                                                 scratch_pool),
3250                             presence_map, ieb->presence,
3251                             kind_map, ieb->kind,
3252                             ieb->record_ancestor_relpath,
3253                             repos_id,
3254                             ieb->recorded_repos_relpath));
3255
3256   if (SVN_IS_VALID_REVNUM(ieb->recorded_peg_revision))
3257     SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, ieb->recorded_peg_revision));
3258
3259   if (SVN_IS_VALID_REVNUM(ieb->recorded_revision))
3260     SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, ieb->recorded_revision));
3261
3262   SVN_ERR(svn_sqlite__insert(NULL, stmt));
3263
3264   return SVN_NO_ERROR;
3265 }
3266
3267 svn_error_t *
3268 svn_wc__db_external_add_file(svn_wc__db_t *db,
3269                              const char *local_abspath,
3270                              const char *wri_abspath,
3271
3272                              const char *repos_relpath,
3273                              const char *repos_root_url,
3274                              const char *repos_uuid,
3275                              svn_revnum_t revision,
3276
3277                              const apr_hash_t *props,
3278                              apr_array_header_t *iprops,
3279
3280                              svn_revnum_t changed_rev,
3281                              apr_time_t changed_date,
3282                              const char *changed_author,
3283
3284                              const svn_checksum_t *checksum,
3285
3286                              const apr_hash_t *dav_cache,
3287
3288                              const char *record_ancestor_abspath,
3289                              const char *recorded_repos_relpath,
3290                              svn_revnum_t recorded_peg_revision,
3291                              svn_revnum_t recorded_revision,
3292
3293                              svn_boolean_t update_actual_props,
3294                              apr_hash_t *new_actual_props,
3295
3296                              svn_boolean_t keep_recorded_info,
3297                              const svn_skel_t *conflict,
3298                              const svn_skel_t *work_items,
3299                              apr_pool_t *scratch_pool)
3300 {
3301   svn_wc__db_wcroot_t *wcroot;
3302   const char *local_relpath;
3303   insert_external_baton_t ieb;
3304
3305   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3306
3307   if (! wri_abspath)
3308     wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3309
3310   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3311                               wri_abspath, scratch_pool, scratch_pool));
3312   VERIFY_USABLE_WCROOT(wcroot);
3313
3314   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3315                                         record_ancestor_abspath));
3316
3317   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3318
3319   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3320
3321   blank_ieb(&ieb);
3322
3323   ieb.kind = svn_node_file;
3324   ieb.presence = svn_wc__db_status_normal;
3325
3326   ieb.repos_root_url = repos_root_url;
3327   ieb.repos_uuid = repos_uuid;
3328
3329   ieb.repos_relpath = repos_relpath;
3330   ieb.revision = revision;
3331
3332   ieb.props = props;
3333   ieb.iprops = iprops;
3334
3335   ieb.changed_rev = changed_rev;
3336   ieb.changed_date = changed_date;
3337   ieb.changed_author = changed_author;
3338
3339   ieb.checksum = checksum;
3340
3341   ieb.dav_cache = dav_cache;
3342
3343   ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3344                                                 wcroot->abspath,
3345                                                 record_ancestor_abspath);
3346   ieb.recorded_repos_relpath = recorded_repos_relpath;
3347   ieb.recorded_peg_revision = recorded_peg_revision;
3348   ieb.recorded_revision = recorded_revision;
3349
3350   ieb.update_actual_props = update_actual_props;
3351   ieb.new_actual_props = new_actual_props;
3352
3353   ieb.keep_recorded_info = keep_recorded_info;
3354
3355   ieb.conflict = conflict;
3356   ieb.work_items = work_items;
3357
3358   SVN_WC__DB_WITH_TXN(
3359             insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3360             wcroot);
3361
3362   return SVN_NO_ERROR;
3363 }
3364
3365 svn_error_t *
3366 svn_wc__db_external_add_symlink(svn_wc__db_t *db,
3367                                 const char *local_abspath,
3368                                 const char *wri_abspath,
3369                                 const char *repos_relpath,
3370                                 const char *repos_root_url,
3371                                 const char *repos_uuid,
3372                                 svn_revnum_t revision,
3373                                 const apr_hash_t *props,
3374                                 svn_revnum_t changed_rev,
3375                                 apr_time_t changed_date,
3376                                 const char *changed_author,
3377                                 const char *target,
3378                                 const apr_hash_t *dav_cache,
3379                                 const char *record_ancestor_abspath,
3380                                 const char *recorded_repos_relpath,
3381                                 svn_revnum_t recorded_peg_revision,
3382                                 svn_revnum_t recorded_revision,
3383                                 svn_boolean_t update_actual_props,
3384                                 apr_hash_t *new_actual_props,
3385                                 svn_boolean_t keep_recorded_info,
3386                                 const svn_skel_t *work_items,
3387                                 apr_pool_t *scratch_pool)
3388 {
3389   svn_wc__db_wcroot_t *wcroot;
3390   const char *local_relpath;
3391   insert_external_baton_t ieb;
3392
3393   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3394
3395   if (! wri_abspath)
3396     wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3397
3398   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3399                               wri_abspath, scratch_pool, scratch_pool));
3400   VERIFY_USABLE_WCROOT(wcroot);
3401
3402   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3403                                         record_ancestor_abspath));
3404
3405   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3406
3407   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3408
3409   blank_ieb(&ieb);
3410
3411   ieb.kind = svn_node_symlink;
3412   ieb.presence = svn_wc__db_status_normal;
3413
3414   ieb.repos_root_url = repos_root_url;
3415   ieb.repos_uuid = repos_uuid;
3416
3417   ieb.repos_relpath = repos_relpath;
3418   ieb.revision = revision;
3419
3420   ieb.props = props;
3421
3422   ieb.changed_rev = changed_rev;
3423   ieb.changed_date = changed_date;
3424   ieb.changed_author = changed_author;
3425
3426   ieb.target = target;
3427
3428   ieb.dav_cache = dav_cache;
3429
3430   ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3431                                                 wcroot->abspath,
3432                                                 record_ancestor_abspath);
3433   ieb.recorded_repos_relpath = recorded_repos_relpath;
3434   ieb.recorded_peg_revision = recorded_peg_revision;
3435   ieb.recorded_revision = recorded_revision;
3436
3437   ieb.update_actual_props = update_actual_props;
3438   ieb.new_actual_props = new_actual_props;
3439
3440   ieb.keep_recorded_info = keep_recorded_info;
3441
3442   ieb.work_items = work_items;
3443
3444   SVN_WC__DB_WITH_TXN(
3445             insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3446             wcroot);
3447
3448   return SVN_NO_ERROR;
3449 }
3450
3451 svn_error_t *
3452 svn_wc__db_external_add_dir(svn_wc__db_t *db,
3453                             const char *local_abspath,
3454                             const char *wri_abspath,
3455                             const char *repos_root_url,
3456                             const char *repos_uuid,
3457                             const char *record_ancestor_abspath,
3458                             const char *recorded_repos_relpath,
3459                             svn_revnum_t recorded_peg_revision,
3460                             svn_revnum_t recorded_revision,
3461                             const svn_skel_t *work_items,
3462                             apr_pool_t *scratch_pool)
3463 {
3464   svn_wc__db_wcroot_t *wcroot;
3465   const char *local_relpath;
3466   insert_external_baton_t ieb;
3467
3468   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3469
3470   if (! wri_abspath)
3471     wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3472
3473   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3474                               wri_abspath, scratch_pool, scratch_pool));
3475   VERIFY_USABLE_WCROOT(wcroot);
3476
3477   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath,
3478                                         record_ancestor_abspath));
3479
3480   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3481
3482   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3483
3484   blank_ieb(&ieb);
3485
3486   ieb.kind = svn_node_dir;
3487   ieb.presence = svn_wc__db_status_normal;
3488
3489   ieb.repos_root_url = repos_root_url;
3490   ieb.repos_uuid = repos_uuid;
3491
3492   ieb.record_ancestor_relpath = svn_dirent_skip_ancestor(
3493                                                 wcroot->abspath,
3494                                                 record_ancestor_abspath);
3495   ieb.recorded_repos_relpath = recorded_repos_relpath;
3496   ieb.recorded_peg_revision = recorded_peg_revision;
3497   ieb.recorded_revision = recorded_revision;
3498
3499   ieb.work_items = work_items;
3500
3501   SVN_WC__DB_WITH_TXN(
3502             insert_external_node(&ieb, wcroot, local_relpath, scratch_pool),
3503             wcroot);
3504
3505   return SVN_NO_ERROR;
3506 }
3507
3508 /* The body of svn_wc__db_external_remove(). */
3509 static svn_error_t *
3510 db_external_remove(const svn_skel_t *work_items,
3511                    svn_wc__db_wcroot_t *wcroot,
3512                    const char *local_relpath,
3513                    apr_pool_t *scratch_pool)
3514 {
3515   svn_sqlite__stmt_t *stmt;
3516   int affected_rows;
3517
3518   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3519                                     STMT_DELETE_EXTERNAL));
3520   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3521   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
3522
3523   if (!affected_rows)
3524     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3525                              _("The node '%s' is not an external."),
3526                              path_for_error_message(wcroot, local_relpath,
3527                                                     scratch_pool));
3528
3529   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
3530
3531   /* ### What about actual? */
3532   return SVN_NO_ERROR;
3533 }
3534
3535 svn_error_t *
3536 svn_wc__db_external_remove(svn_wc__db_t *db,
3537                            const char *local_abspath,
3538                            const char *wri_abspath,
3539                            const svn_skel_t *work_items,
3540                            apr_pool_t *scratch_pool)
3541 {
3542   svn_wc__db_wcroot_t *wcroot;
3543   const char *local_relpath;
3544
3545   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3546
3547   if (! wri_abspath)
3548     wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3549
3550   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3551                               wri_abspath, scratch_pool, scratch_pool));
3552   VERIFY_USABLE_WCROOT(wcroot);
3553
3554   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3555
3556   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3557
3558   SVN_WC__DB_WITH_TXN(db_external_remove(work_items, wcroot, local_relpath,
3559                                          scratch_pool),
3560                       wcroot);
3561
3562   return SVN_NO_ERROR;
3563 }
3564
3565 svn_error_t *
3566 svn_wc__db_external_read(svn_wc__db_status_t *status,
3567                          svn_node_kind_t *kind,
3568                          const char **definining_abspath,
3569                          const char **repos_root_url,
3570                          const char **repos_uuid,
3571                          const char **recorded_repos_relpath,
3572                          svn_revnum_t *recorded_peg_revision,
3573                          svn_revnum_t *recorded_revision,
3574                          svn_wc__db_t *db,
3575                          const char *local_abspath,
3576                          const char *wri_abspath,
3577                          apr_pool_t *result_pool,
3578                          apr_pool_t *scratch_pool)
3579 {
3580   svn_wc__db_wcroot_t *wcroot;
3581   const char *local_relpath;
3582   svn_sqlite__stmt_t *stmt;
3583   svn_boolean_t have_info;
3584   svn_error_t *err = NULL;
3585   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3586
3587   if (! wri_abspath)
3588     wri_abspath = svn_dirent_dirname(local_abspath, scratch_pool);
3589
3590   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3591                               wri_abspath, scratch_pool, scratch_pool));
3592   VERIFY_USABLE_WCROOT(wcroot);
3593
3594   SVN_ERR_ASSERT(svn_dirent_is_ancestor(wcroot->abspath, local_abspath));
3595
3596   local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
3597
3598   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3599                                     STMT_SELECT_EXTERNAL_INFO));
3600   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3601   SVN_ERR(svn_sqlite__step(&have_info, stmt));
3602
3603   if (have_info)
3604     {
3605       if (status)
3606         *status = svn_sqlite__column_token(stmt, 0, presence_map);
3607
3608       if (kind)
3609         *kind = svn_sqlite__column_token(stmt, 1, kind_map);
3610
3611       if (definining_abspath)
3612         {
3613           const char *record_relpath = svn_sqlite__column_text(stmt, 2, NULL);
3614
3615           *definining_abspath = svn_dirent_join(wcroot->abspath,
3616                                                 record_relpath, result_pool);
3617         }
3618
3619       if (repos_root_url || repos_uuid)
3620         {
3621           apr_int64_t repos_id;
3622
3623           repos_id = svn_sqlite__column_int64(stmt, 3);
3624
3625           err = svn_error_compose_create(
3626                         err,
3627                         svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
3628                                                     wcroot, repos_id,
3629                                                     result_pool));
3630         }
3631
3632       if (recorded_repos_relpath)
3633         *recorded_repos_relpath = svn_sqlite__column_text(stmt, 4,
3634                                                           result_pool);
3635
3636       if (recorded_peg_revision)
3637         *recorded_peg_revision = svn_sqlite__column_revnum(stmt, 5);
3638
3639       if (recorded_revision)
3640         *recorded_revision = svn_sqlite__column_revnum(stmt, 6);
3641     }
3642   else
3643     {
3644       err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
3645                               _("The node '%s' is not an external."),
3646                               svn_dirent_local_style(local_abspath,
3647                                                      scratch_pool));
3648     }
3649
3650   return svn_error_trace(
3651                 svn_error_compose_create(err, svn_sqlite__reset(stmt)));
3652 }
3653
3654 svn_error_t *
3655 svn_wc__db_committable_externals_below(apr_array_header_t **externals,
3656                                        svn_wc__db_t *db,
3657                                        const char *local_abspath,
3658                                        svn_boolean_t immediates_only,
3659                                        apr_pool_t *result_pool,
3660                                        apr_pool_t *scratch_pool)
3661 {
3662   svn_wc__db_wcroot_t *wcroot;
3663   svn_sqlite__stmt_t *stmt;
3664   const char *local_relpath;
3665   svn_boolean_t have_row;
3666   svn_wc__committable_external_info_t *info;
3667   svn_node_kind_t db_kind;
3668   apr_array_header_t *result = NULL;
3669
3670   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3671
3672   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3673                               local_abspath, scratch_pool, scratch_pool));
3674   VERIFY_USABLE_WCROOT(wcroot);
3675
3676   SVN_ERR(svn_sqlite__get_statement(
3677                 &stmt, wcroot->sdb,
3678                 immediates_only
3679                     ? STMT_SELECT_COMMITTABLE_EXTERNALS_IMMEDIATELY_BELOW
3680                     : STMT_SELECT_COMMITTABLE_EXTERNALS_BELOW));
3681
3682   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3683
3684   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3685
3686   if (have_row)
3687     result = apr_array_make(result_pool, 0,
3688                             sizeof(svn_wc__committable_external_info_t *));
3689
3690   while (have_row)
3691     {
3692       info = apr_palloc(result_pool, sizeof(*info));
3693
3694       local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3695       info->local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
3696                                             result_pool);
3697
3698       db_kind = svn_sqlite__column_token(stmt, 1, kind_map);
3699       SVN_ERR_ASSERT(db_kind == svn_node_file || db_kind == svn_node_dir);
3700       info->kind = db_kind;
3701
3702       info->repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
3703       info->repos_root_url = svn_sqlite__column_text(stmt, 3, result_pool);
3704
3705       APR_ARRAY_PUSH(result, svn_wc__committable_external_info_t *) = info;
3706
3707       SVN_ERR(svn_sqlite__step(&have_row, stmt));
3708     }
3709
3710   *externals = result;
3711   return svn_error_trace(svn_sqlite__reset(stmt));
3712 }
3713
3714 svn_error_t *
3715 svn_wc__db_externals_defined_below(apr_hash_t **externals,
3716                                    svn_wc__db_t *db,
3717                                    const char *local_abspath,
3718                                    apr_pool_t *result_pool,
3719                                    apr_pool_t *scratch_pool)
3720 {
3721   svn_wc__db_wcroot_t *wcroot;
3722   svn_sqlite__stmt_t *stmt;
3723   const char *local_relpath;
3724   svn_boolean_t have_row;
3725
3726   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3727
3728   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3729                               local_abspath, scratch_pool, scratch_pool));
3730   VERIFY_USABLE_WCROOT(wcroot);
3731
3732   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3733                                     STMT_SELECT_EXTERNALS_DEFINED));
3734
3735   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3736
3737   *externals = apr_hash_make(result_pool);
3738   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3739
3740   while (have_row)
3741     {
3742       const char *def_local_relpath;
3743
3744       local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
3745       def_local_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3746
3747       svn_hash_sets(*externals,
3748                     svn_dirent_join(wcroot->abspath, local_relpath,
3749                                     result_pool),
3750                     svn_dirent_join(wcroot->abspath, def_local_relpath,
3751                                     result_pool));
3752
3753       SVN_ERR(svn_sqlite__step(&have_row, stmt));
3754     }
3755
3756   return svn_error_trace(svn_sqlite__reset(stmt));
3757 }
3758
3759 svn_error_t *
3760 svn_wc__db_externals_gather_definitions(apr_hash_t **externals,
3761                                         apr_hash_t **depths,
3762                                         svn_wc__db_t *db,
3763                                         const char *local_abspath,
3764                                         apr_pool_t *result_pool,
3765                                         apr_pool_t *scratch_pool)
3766 {
3767   svn_wc__db_wcroot_t *wcroot;
3768   svn_sqlite__stmt_t *stmt;
3769   const char *local_relpath;
3770   svn_boolean_t have_row;
3771   svn_error_t *err = NULL;
3772   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
3773
3774   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
3775
3776   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
3777                               local_abspath, scratch_pool, iterpool));
3778   VERIFY_USABLE_WCROOT(wcroot);
3779
3780   *externals = apr_hash_make(result_pool);
3781   if (depths != NULL)
3782     *depths = apr_hash_make(result_pool);
3783
3784   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
3785                                     STMT_SELECT_EXTERNAL_PROPERTIES));
3786
3787   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
3788
3789   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3790
3791   while (have_row)
3792     {
3793       apr_hash_t *node_props;
3794       const char *external_value;
3795
3796       svn_pool_clear(iterpool);
3797       err = svn_sqlite__column_properties(&node_props, stmt, 0, iterpool,
3798                                           iterpool);
3799
3800       if (err)
3801         break;
3802
3803       external_value = svn_prop_get_value(node_props, SVN_PROP_EXTERNALS);
3804
3805       if (external_value)
3806         {
3807           const char *node_abspath;
3808           const char *node_relpath = svn_sqlite__column_text(stmt, 1, NULL);
3809
3810           node_abspath = svn_dirent_join(wcroot->abspath, node_relpath,
3811                                          result_pool);
3812
3813           svn_hash_sets(*externals, node_abspath,
3814                         apr_pstrdup(result_pool, external_value));
3815
3816           if (depths)
3817             {
3818               svn_depth_t depth
3819                 = svn_sqlite__column_token_null(stmt, 2, depth_map,
3820                                                 svn_depth_unknown);
3821
3822               svn_hash_sets(*depths, node_abspath,
3823                             /* Use static string */
3824                             svn_token__to_word(depth_map, depth));
3825             }
3826         }
3827
3828       SVN_ERR(svn_sqlite__step(&have_row, stmt));
3829     }
3830
3831   svn_pool_destroy(iterpool);
3832
3833   return svn_error_trace(svn_error_compose_create(err,
3834                                                   svn_sqlite__reset(stmt)));
3835 }
3836
3837 /* Copy the ACTUAL data for SRC_RELPATH and tweak it to refer to DST_RELPATH.
3838    The new ACTUAL data won't have any conflicts. */
3839 static svn_error_t *
3840 copy_actual(svn_wc__db_wcroot_t *src_wcroot,
3841             const char *src_relpath,
3842             svn_wc__db_wcroot_t *dst_wcroot,
3843             const char *dst_relpath,
3844             apr_pool_t *scratch_pool)
3845 {
3846   svn_sqlite__stmt_t *stmt;
3847   svn_boolean_t have_row;
3848
3849   SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
3850                                     STMT_SELECT_ACTUAL_NODE));
3851   SVN_ERR(svn_sqlite__bindf(stmt, "is", src_wcroot->wc_id, src_relpath));
3852   SVN_ERR(svn_sqlite__step(&have_row, stmt));
3853   if (have_row)
3854     {
3855       apr_size_t props_size;
3856       const char *changelist;
3857       const char *properties;
3858
3859       /* Skipping conflict data... */
3860       changelist = svn_sqlite__column_text(stmt, 0, scratch_pool);
3861       /* No need to parse the properties when simply copying. */
3862       properties = svn_sqlite__column_blob(stmt, 1, &props_size, scratch_pool);
3863
3864       if (changelist || properties)
3865         {
3866           SVN_ERR(svn_sqlite__reset(stmt));
3867
3868           SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
3869                                             STMT_INSERT_ACTUAL_NODE));
3870           SVN_ERR(svn_sqlite__bindf(stmt, "issbs",
3871                                     dst_wcroot->wc_id, dst_relpath,
3872                                 svn_relpath_dirname(dst_relpath, scratch_pool),
3873                                     properties, props_size, changelist));
3874           SVN_ERR(svn_sqlite__step(&have_row, stmt));
3875         }
3876     }
3877   SVN_ERR(svn_sqlite__reset(stmt));
3878
3879   return SVN_NO_ERROR;
3880 }
3881
3882 /* Helper for svn_wc__db_op_copy to handle copying from one db to
3883    another */
3884 static svn_error_t *
3885 cross_db_copy(svn_wc__db_wcroot_t *src_wcroot,
3886               const char *src_relpath,
3887               svn_wc__db_wcroot_t *dst_wcroot,
3888               const char *dst_relpath,
3889               svn_wc__db_status_t dst_status,
3890               int dst_op_depth,
3891               int dst_np_op_depth,
3892               svn_node_kind_t kind,
3893               const apr_array_header_t *children,
3894               apr_int64_t copyfrom_id,
3895               const char *copyfrom_relpath,
3896               svn_revnum_t copyfrom_rev,
3897               apr_pool_t *scratch_pool)
3898 {
3899   insert_working_baton_t iwb;
3900   svn_revnum_t changed_rev;
3901   apr_time_t changed_date;
3902   const char *changed_author;
3903   const svn_checksum_t *checksum;
3904   apr_hash_t *props;
3905   svn_depth_t depth;
3906
3907   SVN_ERR_ASSERT(kind == svn_node_file
3908                  || kind == svn_node_dir
3909                  );
3910
3911   SVN_ERR(read_info(NULL, NULL, NULL, NULL, NULL,
3912                     &changed_rev, &changed_date, &changed_author, &depth,
3913                     &checksum, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3914                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
3915                     src_wcroot, src_relpath, scratch_pool, scratch_pool));
3916
3917   if (dst_status != svn_wc__db_status_not_present
3918       && dst_status != svn_wc__db_status_excluded
3919       && dst_status != svn_wc__db_status_server_excluded)
3920     {
3921       SVN_ERR(db_read_pristine_props(&props, src_wcroot, src_relpath, FALSE,
3922                                      scratch_pool, scratch_pool));
3923     }
3924   else
3925     props = NULL;
3926
3927   blank_iwb(&iwb);
3928   iwb.presence = dst_status;
3929   iwb.kind = kind;
3930
3931   iwb.props = props;
3932   iwb.changed_rev = changed_rev;
3933   iwb.changed_date = changed_date;
3934   iwb.changed_author = changed_author;
3935   iwb.original_repos_id = copyfrom_id;
3936   iwb.original_repos_relpath = copyfrom_relpath;
3937   iwb.original_revnum = copyfrom_rev;
3938   iwb.moved_here = FALSE;
3939
3940   iwb.op_depth = dst_op_depth;
3941
3942   iwb.checksum = checksum;
3943   iwb.children = children;
3944   iwb.depth = depth;
3945
3946   iwb.not_present_op_depth = dst_np_op_depth;
3947
3948   SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath, scratch_pool));
3949
3950   SVN_ERR(copy_actual(src_wcroot, src_relpath,
3951                       dst_wcroot, dst_relpath, scratch_pool));
3952
3953   return SVN_NO_ERROR;
3954 }
3955
3956 /* Helper for scan_deletion_txn. Extracts the moved-to information, if
3957    any, from STMT.  Sets *SCAN to FALSE if moved-to was available. */
3958 static svn_error_t *
3959 get_moved_to(const char **moved_to_relpath_p,
3960              const char **moved_to_op_root_relpath_p,
3961              svn_boolean_t *scan,
3962              svn_sqlite__stmt_t *stmt,
3963              const char *current_relpath,
3964              svn_wc__db_wcroot_t *wcroot,
3965              const char *local_relpath,
3966              apr_pool_t *result_pool,
3967              apr_pool_t *scratch_pool)
3968 {
3969   const char *moved_to_relpath = svn_sqlite__column_text(stmt, 3, NULL);
3970
3971   if (moved_to_relpath)
3972     {
3973       const char *moved_to_op_root_relpath = moved_to_relpath;
3974
3975       if (strcmp(current_relpath, local_relpath))
3976         {
3977           /* LOCAL_RELPATH is a child inside the move op-root. */
3978           const char *moved_child_relpath;
3979
3980           /* The CURRENT_RELPATH is the op_root of the delete-half of
3981            * the move. LOCAL_RELPATH is a child that was moved along.
3982            * Compute the child's new location within the move target. */
3983           moved_child_relpath = svn_relpath_skip_ancestor(current_relpath,
3984                                                           local_relpath);
3985           SVN_ERR_ASSERT(moved_child_relpath &&
3986                          strlen(moved_child_relpath) > 0);
3987           moved_to_relpath = svn_relpath_join(moved_to_op_root_relpath,
3988                                               moved_child_relpath,
3989                                               result_pool);
3990         }
3991
3992       if (moved_to_op_root_relpath && moved_to_op_root_relpath_p)
3993         *moved_to_op_root_relpath_p
3994           = apr_pstrdup(result_pool, moved_to_op_root_relpath);
3995
3996       if (moved_to_relpath && moved_to_relpath_p)
3997         *moved_to_relpath_p
3998           = apr_pstrdup(result_pool, moved_to_relpath);
3999
4000       *scan = FALSE;
4001     }
4002
4003   return SVN_NO_ERROR;
4004 }
4005
4006
4007 /* The body of svn_wc__db_scan_deletion().
4008  */
4009 static svn_error_t *
4010 scan_deletion(const char **base_del_relpath,
4011               const char **moved_to_relpath,
4012               const char **work_del_relpath,
4013               const char **moved_to_op_root_relpath,
4014               svn_wc__db_wcroot_t *wcroot,
4015               const char *local_relpath,
4016               apr_pool_t *result_pool,
4017               apr_pool_t *scratch_pool)
4018 {
4019   const char *current_relpath = local_relpath;
4020   svn_sqlite__stmt_t *stmt;
4021   svn_wc__db_status_t work_presence;
4022   svn_boolean_t have_row, scan, have_base;
4023   int op_depth;
4024
4025   /* Initialize all the OUT parameters.  */
4026   if (base_del_relpath != NULL)
4027     *base_del_relpath = NULL;
4028   if (moved_to_relpath != NULL)
4029     *moved_to_relpath = NULL;
4030   if (work_del_relpath != NULL)
4031     *work_del_relpath = NULL;
4032   if (moved_to_op_root_relpath != NULL)
4033     *moved_to_op_root_relpath = NULL;
4034
4035   /* If looking for moved-to info then we need to scan every path
4036      until we find it.  If not looking for moved-to we only need to
4037      check op-roots and parents of op-roots. */
4038   scan = (moved_to_op_root_relpath || moved_to_relpath);
4039
4040   SVN_ERR(svn_sqlite__get_statement(
4041                     &stmt, wcroot->sdb, STMT_SELECT_DELETION_INFO));
4042
4043   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, current_relpath));
4044   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4045   if (!have_row)
4046     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
4047                              _("The node '%s' was not found."),
4048                              path_for_error_message(wcroot, local_relpath,
4049                                                     scratch_pool));
4050
4051   work_presence = svn_sqlite__column_token(stmt, 1, presence_map);
4052   have_base = !svn_sqlite__column_is_null(stmt, 0);
4053   if (work_presence != svn_wc__db_status_not_present
4054       && work_presence != svn_wc__db_status_base_deleted)
4055     return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
4056                              svn_sqlite__reset(stmt),
4057                              _("Expected node '%s' to be deleted."),
4058                              path_for_error_message(wcroot, local_relpath,
4059                                                     scratch_pool));
4060
4061   op_depth = svn_sqlite__column_int(stmt, 2);
4062
4063   /* Special case: LOCAL_RELPATH not-present within a WORKING tree, we
4064      treat this as an op-root.  At commit time we need to explicitly
4065      delete such nodes otherwise they will be present in the
4066      repository copy. */
4067   if (work_presence == svn_wc__db_status_not_present
4068       && work_del_relpath && !*work_del_relpath)
4069     {
4070       *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
4071
4072       if (!scan && !base_del_relpath)
4073         {
4074           /* We have all we need, exit early */
4075           SVN_ERR(svn_sqlite__reset(stmt));
4076           return SVN_NO_ERROR;
4077         }
4078     }
4079
4080
4081   while (TRUE)
4082     {
4083       svn_error_t *err;
4084       const char *parent_relpath;
4085       int current_depth = relpath_depth(current_relpath);
4086
4087       /* Step CURRENT_RELPATH to op-root */
4088
4089       while (TRUE)
4090         {
4091           if (scan)
4092             {
4093               err = get_moved_to(moved_to_relpath, moved_to_op_root_relpath,
4094                                  &scan, stmt, current_relpath,
4095                                  wcroot, local_relpath,
4096                                  result_pool, scratch_pool);
4097               if (err || (!scan
4098                           && !base_del_relpath
4099                           && !work_del_relpath))
4100                 {
4101                   /* We have all we need (or an error occurred) */
4102                   SVN_ERR(svn_sqlite__reset(stmt));
4103                   return svn_error_trace(err);
4104                 }
4105             }
4106
4107           if (current_depth <= op_depth)
4108             break;
4109
4110           current_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4111           --current_depth;
4112
4113           if (scan || current_depth == op_depth)
4114             {
4115               SVN_ERR(svn_sqlite__reset(stmt));
4116               SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
4117                                         current_relpath));
4118               SVN_ERR(svn_sqlite__step(&have_row, stmt));
4119               SVN_ERR_ASSERT(have_row);
4120               have_base = !svn_sqlite__column_is_null(stmt, 0);
4121             }
4122         }
4123       SVN_ERR(svn_sqlite__reset(stmt));
4124
4125       /* Now CURRENT_RELPATH is an op-root, have a look at the parent. */
4126
4127       SVN_ERR_ASSERT(current_relpath[0] != '\0'); /* Catch invalid data */
4128       parent_relpath = svn_relpath_dirname(current_relpath, scratch_pool);
4129       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4130       SVN_ERR(svn_sqlite__step(&have_row, stmt));
4131       if (!have_row)
4132         {
4133           /* No row means no WORKING node which mean we just fell off
4134              the WORKING tree, so CURRENT_RELPATH is the op-root
4135              closest to the wc root. */
4136           if (have_base && base_del_relpath)
4137             *base_del_relpath = apr_pstrdup(result_pool, current_relpath);
4138           break;
4139         }
4140
4141       /* Still in the WORKING tree so the first time we get here
4142          CURRENT_RELPATH is a delete op-root in the WORKING tree. */
4143       if (work_del_relpath && !*work_del_relpath)
4144         {
4145           *work_del_relpath = apr_pstrdup(result_pool, current_relpath);
4146
4147           if (!scan && !base_del_relpath)
4148             break; /* We have all we need */
4149         }
4150
4151       current_relpath = parent_relpath;
4152       op_depth = svn_sqlite__column_int(stmt, 2);
4153       have_base = !svn_sqlite__column_is_null(stmt, 0);
4154     }
4155
4156   SVN_ERR(svn_sqlite__reset(stmt));
4157
4158   return SVN_NO_ERROR;
4159 }
4160
4161 svn_error_t *
4162 svn_wc__db_scan_deletion_internal(
4163               const char **base_del_relpath,
4164               const char **moved_to_relpath,
4165               const char **work_del_relpath,
4166               const char **moved_to_op_root_relpath,
4167               svn_wc__db_wcroot_t *wcroot,
4168               const char *local_relpath,
4169               apr_pool_t *result_pool,
4170               apr_pool_t *scratch_pool)
4171 {
4172   return svn_error_trace(
4173             scan_deletion(base_del_relpath, moved_to_relpath, work_del_relpath,
4174                           moved_to_op_root_relpath,
4175                           wcroot, local_relpath,
4176                           result_pool, scratch_pool));
4177 }
4178
4179
4180 svn_error_t *
4181 svn_wc__db_scan_deletion(const char **base_del_abspath,
4182                          const char **moved_to_abspath,
4183                          const char **work_del_abspath,
4184                          const char **moved_to_op_root_abspath,
4185                          svn_wc__db_t *db,
4186                          const char *local_abspath,
4187                          apr_pool_t *result_pool,
4188                          apr_pool_t *scratch_pool)
4189 {
4190   svn_wc__db_wcroot_t *wcroot;
4191   const char *local_relpath;
4192   const char *base_del_relpath, *moved_to_relpath, *work_del_relpath;
4193   const char *moved_to_op_root_relpath;
4194
4195   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
4196
4197   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
4198                               local_abspath, scratch_pool, scratch_pool));
4199   VERIFY_USABLE_WCROOT(wcroot);
4200
4201   SVN_WC__DB_WITH_TXN(
4202     scan_deletion(&base_del_relpath, &moved_to_relpath,
4203                   &work_del_relpath, &moved_to_op_root_relpath,
4204                   wcroot, local_relpath, result_pool, scratch_pool),
4205     wcroot);
4206
4207   if (base_del_abspath)
4208     {
4209       *base_del_abspath = (base_del_relpath
4210                            ? svn_dirent_join(wcroot->abspath,
4211                                              base_del_relpath, result_pool)
4212                            : NULL);
4213     }
4214   if (moved_to_abspath)
4215     {
4216       *moved_to_abspath = (moved_to_relpath
4217                            ? svn_dirent_join(wcroot->abspath,
4218                                              moved_to_relpath, result_pool)
4219                            : NULL);
4220     }
4221   if (work_del_abspath)
4222     {
4223       *work_del_abspath = (work_del_relpath
4224                            ? svn_dirent_join(wcroot->abspath,
4225                                              work_del_relpath, result_pool)
4226                            : NULL);
4227     }
4228   if (moved_to_op_root_abspath)
4229     {
4230       *moved_to_op_root_abspath = (moved_to_op_root_relpath
4231                            ? svn_dirent_join(wcroot->abspath,
4232                                              moved_to_op_root_relpath,
4233                                              result_pool)
4234                            : NULL);
4235     }
4236
4237   return SVN_NO_ERROR;
4238 }
4239
4240
4241 /* Set *COPYFROM_ID, *COPYFROM_RELPATH, *COPYFROM_REV to the values
4242    appropriate for the copy. Also return *STATUS, *KIND and *HAVE_WORK, *OP_ROOT
4243    since they are available.  This is a helper for
4244    svn_wc__db_op_copy. */
4245 static svn_error_t *
4246 get_info_for_copy(apr_int64_t *copyfrom_id,
4247                   const char **copyfrom_relpath,
4248                   svn_revnum_t *copyfrom_rev,
4249                   svn_wc__db_status_t *status,
4250                   svn_node_kind_t *kind,
4251                   svn_boolean_t *op_root,
4252                   svn_wc__db_wcroot_t *src_wcroot,
4253                   const char *local_relpath,
4254                   svn_wc__db_wcroot_t *dst_wcroot,
4255                   apr_pool_t *result_pool,
4256                   apr_pool_t *scratch_pool)
4257 {
4258   const char *repos_relpath;
4259   svn_revnum_t revision;
4260   svn_wc__db_status_t node_status;
4261   apr_int64_t repos_id;
4262   svn_boolean_t is_op_root;
4263
4264   SVN_ERR(read_info(&node_status, kind, &revision, &repos_relpath, &repos_id,
4265                     NULL, NULL, NULL, NULL, NULL, NULL, copyfrom_relpath,
4266                     copyfrom_id, copyfrom_rev, NULL, NULL, NULL, NULL,
4267                     NULL, &is_op_root, NULL, NULL,
4268                     NULL /* have_base */,
4269                     NULL /* have_more_work */,
4270                     NULL /* have_work */,
4271                     src_wcroot, local_relpath, result_pool, scratch_pool));
4272
4273   if (op_root)
4274     *op_root = is_op_root;
4275
4276   if (node_status == svn_wc__db_status_excluded)
4277     {
4278       /* The parent cannot be excluded, so look at the parent and then
4279          adjust the relpath */
4280       const char *parent_relpath, *base_name;
4281
4282       svn_dirent_split(&parent_relpath, &base_name, local_relpath,
4283                        scratch_pool);
4284       SVN_ERR(get_info_for_copy(copyfrom_id, copyfrom_relpath, copyfrom_rev,
4285                                 NULL, NULL, NULL,
4286                                 src_wcroot, parent_relpath, dst_wcroot,
4287                                 scratch_pool, scratch_pool));
4288       if (*copyfrom_relpath)
4289         *copyfrom_relpath = svn_relpath_join(*copyfrom_relpath, base_name,
4290                                              result_pool);
4291     }
4292   else if (node_status == svn_wc__db_status_added)
4293     {
4294       SVN_ERR(scan_addition(&node_status, NULL, NULL, NULL, NULL, NULL, NULL,
4295                             NULL, NULL, NULL, src_wcroot, local_relpath,
4296                             scratch_pool, scratch_pool));
4297     }
4298   else if (node_status == svn_wc__db_status_deleted && is_op_root)
4299     {
4300       const char *base_del_relpath, *work_del_relpath;
4301
4302       SVN_ERR(scan_deletion(&base_del_relpath, NULL,
4303                             &work_del_relpath,
4304                             NULL, src_wcroot, local_relpath,
4305                             scratch_pool, scratch_pool));
4306       if (work_del_relpath)
4307         {
4308           const char *op_root_relpath;
4309           const char *parent_del_relpath = svn_relpath_dirname(work_del_relpath,
4310                                                                scratch_pool);
4311
4312           /* Similar to, but not the same as, the _scan_addition and
4313              _join above.  Can we use get_copyfrom here? */
4314           SVN_ERR(scan_addition(NULL, &op_root_relpath,
4315                                 NULL, NULL, /* repos_* */
4316                                 copyfrom_relpath, copyfrom_id, copyfrom_rev,
4317                                 NULL, NULL, NULL,
4318                                 src_wcroot, parent_del_relpath,
4319                                 scratch_pool, scratch_pool));
4320           *copyfrom_relpath
4321             = svn_relpath_join(*copyfrom_relpath,
4322                                svn_relpath_skip_ancestor(op_root_relpath,
4323                                                          local_relpath),
4324                                result_pool);
4325         }
4326       else if (base_del_relpath)
4327         {
4328           SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, copyfrom_rev,
4329                                                     copyfrom_relpath,
4330                                                     copyfrom_id, NULL, NULL,
4331                                                     NULL, NULL, NULL, NULL,
4332                                                     NULL, NULL, NULL, NULL,
4333                                                     src_wcroot, local_relpath,
4334                                                     result_pool,
4335                                                     scratch_pool));
4336         }
4337       else
4338         SVN_ERR_MALFUNCTION();
4339     }
4340   else if (node_status == svn_wc__db_status_deleted)
4341     {
4342       /* Keep original_* from read_info() to allow seeing the difference
4343          between base-deleted and not present */
4344     }
4345   else
4346     {
4347       *copyfrom_relpath = repos_relpath;
4348       *copyfrom_rev = revision;
4349       *copyfrom_id = repos_id;
4350     }
4351
4352   if (status)
4353     *status = node_status;
4354
4355   if (src_wcroot != dst_wcroot && *copyfrom_relpath)
4356     {
4357       const char *repos_root_url;
4358       const char *repos_uuid;
4359
4360       /* Pass the right repos-id for the destination db. We can't just use
4361          the id of the source database, as this value can change after
4362          relocation (and perhaps also when we start storing multiple
4363          working copies in a single db)! */
4364
4365       SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
4366                                           src_wcroot, *copyfrom_id,
4367                                           scratch_pool));
4368
4369       SVN_ERR(create_repos_id(copyfrom_id, repos_root_url, repos_uuid,
4370                               dst_wcroot->sdb, scratch_pool));
4371     }
4372
4373   return SVN_NO_ERROR;
4374 }
4375
4376
4377 /* Set *OP_DEPTH to the highest op depth of WCROOT:LOCAL_RELPATH. */
4378 static svn_error_t *
4379 op_depth_of(int *op_depth,
4380             svn_wc__db_wcroot_t *wcroot,
4381             const char *local_relpath)
4382 {
4383   svn_sqlite__stmt_t *stmt;
4384   svn_boolean_t have_row;
4385
4386   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4387                                     STMT_SELECT_NODE_INFO));
4388   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4389   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4390   SVN_ERR_ASSERT(have_row);
4391   *op_depth = svn_sqlite__column_int(stmt, 0);
4392   SVN_ERR(svn_sqlite__reset(stmt));
4393
4394   return SVN_NO_ERROR;
4395 }
4396
4397
4398 /* Determine at which OP_DEPTH a copy of COPYFROM_REPOS_ID, COPYFROM_RELPATH at
4399    revision COPYFROM_REVISION should be inserted as LOCAL_RELPATH. Do this
4400    by checking if this would be a direct child of a copy of its parent
4401    directory. If it is then set *OP_DEPTH to the op_depth of its parent.
4402
4403    If the node is not a direct copy at the same revision of the parent
4404    *NP_OP_DEPTH will be set to the op_depth of the parent when a not-present
4405    node should be inserted at this op_depth. This will be the case when the
4406    parent already defined an incomplete child with the same name. Otherwise
4407    *NP_OP_DEPTH will be set to -1.
4408
4409    If the parent node is not the parent of the to be copied node, then
4410    *OP_DEPTH will be set to the proper op_depth for a new operation root.
4411
4412    Set *PARENT_OP_DEPTH to the op_depth of the parent.
4413
4414  */
4415 static svn_error_t *
4416 op_depth_for_copy(int *op_depth,
4417                   int *np_op_depth,
4418                   int *parent_op_depth,
4419                   apr_int64_t copyfrom_repos_id,
4420                   const char *copyfrom_relpath,
4421                   svn_revnum_t copyfrom_revision,
4422                   svn_wc__db_wcroot_t *wcroot,
4423                   const char *local_relpath,
4424                   apr_pool_t *scratch_pool)
4425 {
4426   const char *parent_relpath, *name;
4427   svn_sqlite__stmt_t *stmt;
4428   svn_boolean_t have_row;
4429   int incomplete_op_depth = -1;
4430   int min_op_depth = 1; /* Never touch BASE */
4431
4432   *op_depth = relpath_depth(local_relpath);
4433   *np_op_depth = -1;
4434
4435   svn_relpath_split(&parent_relpath, &name, local_relpath, scratch_pool);
4436   *parent_op_depth = relpath_depth(parent_relpath);
4437
4438   if (!copyfrom_relpath)
4439     return SVN_NO_ERROR;
4440
4441   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4442                                     STMT_SELECT_WORKING_NODE));
4443   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4444   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4445   if (have_row)
4446     {
4447       svn_wc__db_status_t status = svn_sqlite__column_token(stmt, 1,
4448                                                             presence_map);
4449
4450       min_op_depth = svn_sqlite__column_int(stmt, 0);
4451       if (status == svn_wc__db_status_incomplete)
4452         incomplete_op_depth = min_op_depth;
4453     }
4454   SVN_ERR(svn_sqlite__reset(stmt));
4455
4456   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4457                                     STMT_SELECT_WORKING_NODE));
4458   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, parent_relpath));
4459   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4460   if (have_row)
4461     {
4462       svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 1,
4463                                                               presence_map);
4464
4465       *parent_op_depth = svn_sqlite__column_int(stmt, 0);
4466       if (*parent_op_depth < min_op_depth)
4467         {
4468           /* We want to create a copy; not overwrite the lower layers */
4469           SVN_ERR(svn_sqlite__reset(stmt));
4470           return SVN_NO_ERROR;
4471         }
4472
4473       /* You can only add children below a node that exists.
4474          In WORKING that must be status added, which is represented
4475          as presence normal */
4476       SVN_ERR_ASSERT(presence == svn_wc__db_status_normal);
4477
4478       if ((incomplete_op_depth < 0)
4479           || (incomplete_op_depth == *parent_op_depth))
4480         {
4481           apr_int64_t parent_copyfrom_repos_id
4482             = svn_sqlite__column_int64(stmt, 10);
4483           const char *parent_copyfrom_relpath
4484             = svn_sqlite__column_text(stmt, 11, NULL);
4485           svn_revnum_t parent_copyfrom_revision
4486             = svn_sqlite__column_revnum(stmt, 12);
4487
4488           if (parent_copyfrom_repos_id == copyfrom_repos_id)
4489             {
4490               if (copyfrom_revision == parent_copyfrom_revision
4491                   && !strcmp(copyfrom_relpath,
4492                              svn_relpath_join(parent_copyfrom_relpath, name,
4493                                               scratch_pool)))
4494                 *op_depth = *parent_op_depth;
4495               else if (incomplete_op_depth > 0)
4496                 *np_op_depth = incomplete_op_depth;
4497             }
4498         }
4499     }
4500   SVN_ERR(svn_sqlite__reset(stmt));
4501
4502   return SVN_NO_ERROR;
4503 }
4504
4505
4506 /* Like svn_wc__db_op_copy(), but with WCROOT+LOCAL_RELPATH
4507  * instead of DB+LOCAL_ABSPATH. A non-zero MOVE_OP_DEPTH implies that the
4508  * copy operation is part of a move, and indicates the op-depth of the
4509  * move destination op-root. */
4510 static svn_error_t *
4511 db_op_copy(svn_wc__db_wcroot_t *src_wcroot,
4512            const char *src_relpath,
4513            svn_wc__db_wcroot_t *dst_wcroot,
4514            const char *dst_relpath,
4515            const svn_skel_t *work_items,
4516            int move_op_depth,
4517            apr_pool_t *scratch_pool)
4518 {
4519   const char *copyfrom_relpath;
4520   svn_revnum_t copyfrom_rev;
4521   svn_wc__db_status_t status;
4522   svn_wc__db_status_t dst_presence;
4523   svn_boolean_t op_root;
4524   apr_int64_t copyfrom_id;
4525   int dst_op_depth;
4526   int dst_np_op_depth;
4527   int dst_parent_op_depth;
4528   svn_node_kind_t kind;
4529   const apr_array_header_t *children;
4530
4531   SVN_ERR(get_info_for_copy(&copyfrom_id, &copyfrom_relpath, &copyfrom_rev,
4532                             &status, &kind, &op_root,
4533                             src_wcroot, src_relpath, dst_wcroot,
4534                             scratch_pool, scratch_pool));
4535
4536   SVN_ERR(op_depth_for_copy(&dst_op_depth, &dst_np_op_depth,
4537                             &dst_parent_op_depth,
4538                             copyfrom_id, copyfrom_relpath, copyfrom_rev,
4539                             dst_wcroot, dst_relpath, scratch_pool));
4540
4541   SVN_ERR_ASSERT(kind == svn_node_file || kind == svn_node_dir);
4542
4543   /* ### New status, not finished, see notes/wc-ng/copying */
4544   switch (status)
4545     {
4546     case svn_wc__db_status_normal:
4547     case svn_wc__db_status_added:
4548     case svn_wc__db_status_moved_here:
4549     case svn_wc__db_status_copied:
4550       dst_presence = svn_wc__db_status_normal;
4551       break;
4552     case svn_wc__db_status_deleted:
4553       if (op_root)
4554         {
4555           /* If the lower layer is already shadowcopied we can skip adding
4556              a not present node. */
4557           svn_error_t *err;
4558           svn_wc__db_status_t dst_status;
4559
4560           err = read_info(&dst_status, NULL, NULL, NULL, NULL, NULL, NULL,
4561                           NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4562                           NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
4563                           dst_wcroot, dst_relpath, scratch_pool, scratch_pool);
4564
4565           if (err)
4566             {
4567               if (err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
4568                 svn_error_clear(err);
4569               else
4570                 return svn_error_trace(err);
4571             }
4572           else if (dst_status == svn_wc__db_status_deleted)
4573             {
4574               /* Node is already deleted; skip the NODES work, but do
4575                  install wq items if requested */
4576               SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4577                                      scratch_pool));
4578               return SVN_NO_ERROR;
4579             }
4580         }
4581       else
4582         {
4583           /* This node is either a not-present node (which should be copied), or
4584              a base-delete of some lower layer (which shouldn't).
4585              Subversion <= 1.7 always added a not-present node here, which is
4586              safe (as it postpones the hard work until commit time and then we
4587              ask the repository), but it breaks some move scenarios.
4588              */
4589
4590            if (! copyfrom_relpath)
4591              {
4592                SVN_ERR(add_work_items(dst_wcroot->sdb, work_items,
4593                                      scratch_pool));
4594                return SVN_NO_ERROR;
4595              }
4596
4597            /* Fall through. Install not present node */
4598         }
4599     case svn_wc__db_status_not_present:
4600     case svn_wc__db_status_excluded:
4601       /* These presence values should not create a new op depth */
4602       if (dst_np_op_depth > 0)
4603         {
4604           dst_op_depth = dst_np_op_depth;
4605           dst_np_op_depth = -1;
4606         }
4607       if (status == svn_wc__db_status_excluded)
4608         dst_presence = svn_wc__db_status_excluded;
4609       else
4610         dst_presence = svn_wc__db_status_not_present;
4611       break;
4612     case svn_wc__db_status_server_excluded:
4613       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4614                                _("Cannot copy '%s' excluded by server"),
4615                                path_for_error_message(src_wcroot,
4616                                                       src_relpath,
4617                                                       scratch_pool));
4618     default:
4619       /* Perhaps we should allow incomplete to incomplete? We can't
4620          avoid incomplete working nodes as one step in copying a
4621          directory is to add incomplete children. */
4622       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
4623                                _("Cannot handle status of '%s'"),
4624                                path_for_error_message(src_wcroot,
4625                                                       src_relpath,
4626                                                       scratch_pool));
4627     }
4628
4629   if (kind == svn_node_dir)
4630     {
4631       int src_op_depth;
4632
4633       SVN_ERR(op_depth_of(&src_op_depth, src_wcroot, src_relpath));
4634       SVN_ERR(gather_children(&children, src_wcroot, src_relpath,
4635                               STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth,
4636                               scratch_pool, scratch_pool));
4637     }
4638   else
4639     children = NULL;
4640
4641   if (src_wcroot == dst_wcroot)
4642     {
4643       svn_sqlite__stmt_t *stmt;
4644       const char *dst_parent_relpath = svn_relpath_dirname(dst_relpath,
4645                                                            scratch_pool);
4646
4647       SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
4648                                         STMT_INSERT_WORKING_NODE_COPY_FROM));
4649
4650       SVN_ERR(svn_sqlite__bindf(stmt, "issdst",
4651                     src_wcroot->wc_id, src_relpath,
4652                     dst_relpath,
4653                     dst_op_depth,
4654                     dst_parent_relpath,
4655                     presence_map, dst_presence));
4656
4657       if (move_op_depth > 0)
4658         {
4659           if (relpath_depth(dst_relpath) == move_op_depth)
4660             {
4661               /* We're moving the root of the move operation.
4662                *
4663                * When an added node or the op-root of a copy is moved,
4664                * there is no 'moved-from' corresponding to the moved-here
4665                * node. So the net effect is the same as copy+delete.
4666                * Perform a normal copy operation in these cases. */
4667               if (!(status == svn_wc__db_status_added ||
4668                     (status == svn_wc__db_status_copied && op_root)))
4669                 SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4670             }
4671           else
4672             {
4673               svn_sqlite__stmt_t *info_stmt;
4674               svn_boolean_t have_row;
4675
4676               /* We're moving a child along with the root of the move.
4677                *
4678                * Set moved-here depending on dst_parent, propagating the
4679                * above decision to moved-along children at the same op_depth.
4680                * We can't use scan_addition() to detect moved-here because
4681                * the delete-half of the move might not yet exist. */
4682               SVN_ERR(svn_sqlite__get_statement(&info_stmt, dst_wcroot->sdb,
4683                                                 STMT_SELECT_NODE_INFO));
4684               SVN_ERR(svn_sqlite__bindf(info_stmt, "is", dst_wcroot->wc_id,
4685                                         dst_parent_relpath));
4686               SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4687               SVN_ERR_ASSERT(have_row);
4688               if (svn_sqlite__column_boolean(info_stmt, 15) &&
4689                   dst_op_depth == dst_parent_op_depth)
4690                 {
4691                   SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4692                   SVN_ERR(svn_sqlite__reset(info_stmt));
4693                 }
4694               else
4695                 {
4696                   SVN_ERR(svn_sqlite__reset(info_stmt));
4697
4698                   /* If the child has been moved into the tree we're moving,
4699                    * keep its moved-here bit set. */
4700                   SVN_ERR(svn_sqlite__get_statement(&info_stmt,
4701                                                     dst_wcroot->sdb,
4702                                                     STMT_SELECT_NODE_INFO));
4703                   SVN_ERR(svn_sqlite__bindf(info_stmt, "is",
4704                                             dst_wcroot->wc_id, src_relpath));
4705                   SVN_ERR(svn_sqlite__step(&have_row, info_stmt));
4706                   SVN_ERR_ASSERT(have_row);
4707                   if (svn_sqlite__column_boolean(info_stmt, 15))
4708                     SVN_ERR(svn_sqlite__bind_int(stmt, 7, 1));
4709                   SVN_ERR(svn_sqlite__reset(info_stmt));
4710                 }
4711             }
4712         }
4713
4714       SVN_ERR(svn_sqlite__step_done(stmt));
4715
4716       /* ### Copying changelist is OK for a move but what about a copy? */
4717       SVN_ERR(copy_actual(src_wcroot, src_relpath,
4718                           dst_wcroot, dst_relpath, scratch_pool));
4719
4720       if (dst_np_op_depth > 0)
4721         {
4722           /* We introduce a not-present node at the parent's op_depth to
4723              properly start a new op-depth at our own op_depth. This marks
4724              us as an op_root for commit and allows reverting just this
4725              operation */
4726
4727           SVN_ERR(svn_sqlite__get_statement(&stmt, dst_wcroot->sdb,
4728                                             STMT_INSERT_NODE));
4729           SVN_ERR(svn_sqlite__bindf(stmt, "isdsisrtnt",
4730                                     src_wcroot->wc_id, dst_relpath,
4731                                     dst_np_op_depth, dst_parent_relpath,
4732                                     copyfrom_id, copyfrom_relpath,
4733                                     copyfrom_rev,
4734                                     presence_map,
4735                                        svn_wc__db_status_not_present,
4736                                     /* NULL */
4737                                     kind_map, kind));
4738
4739           SVN_ERR(svn_sqlite__step_done(stmt));
4740         }
4741       /* Insert incomplete children, if relevant.
4742          The children are part of the same op and so have the same op_depth.
4743          (The only time we'd want a different depth is during a recursive
4744          simple add, but we never insert children here during a simple add.) */
4745       if (kind == svn_node_dir
4746           && dst_presence == svn_wc__db_status_normal)
4747         SVN_ERR(insert_incomplete_children(
4748                   dst_wcroot->sdb,
4749                   dst_wcroot->wc_id,
4750                   dst_relpath,
4751                   copyfrom_id,
4752                   copyfrom_relpath,
4753                   copyfrom_rev,
4754                   children,
4755                   dst_op_depth,
4756                   scratch_pool));
4757     }
4758   else
4759     {
4760       SVN_ERR(cross_db_copy(src_wcroot, src_relpath, dst_wcroot,
4761                             dst_relpath, dst_presence, dst_op_depth,
4762                             dst_np_op_depth, kind,
4763                             children, copyfrom_id, copyfrom_relpath,
4764                             copyfrom_rev, scratch_pool));
4765     }
4766
4767   SVN_ERR(add_work_items(dst_wcroot->sdb, work_items, scratch_pool));
4768
4769   return SVN_NO_ERROR;
4770 }
4771
4772 /* Baton for passing args to op_copy_txn(). */
4773 struct op_copy_baton
4774 {
4775   svn_wc__db_wcroot_t *src_wcroot;
4776   const char *src_relpath;
4777
4778   svn_wc__db_wcroot_t *dst_wcroot;
4779   const char *dst_relpath;
4780
4781   const svn_skel_t *work_items;
4782
4783   svn_boolean_t is_move;
4784   const char *dst_op_root_relpath;
4785 };
4786
4787 /* Helper for svn_wc__db_op_copy(). */
4788 static svn_error_t *
4789 op_copy_txn(svn_wc__db_wcroot_t *wcroot,
4790             struct op_copy_baton *ocb,
4791             apr_pool_t *scratch_pool)
4792 {
4793   int move_op_depth;
4794
4795   if (wcroot != ocb->dst_wcroot)
4796     {
4797        /* Source and destination databases differ; so also start a lock
4798           in the destination database, by calling ourself in an extra lock. */
4799
4800       SVN_WC__DB_WITH_TXN(op_copy_txn(ocb->dst_wcroot, ocb, scratch_pool),
4801                           ocb->dst_wcroot);
4802
4803       return SVN_NO_ERROR;
4804     }
4805
4806   /* From this point we can assume a lock in the src and dst databases */
4807
4808   if (ocb->is_move)
4809     move_op_depth = relpath_depth(ocb->dst_op_root_relpath);
4810   else
4811     move_op_depth = 0;
4812
4813   SVN_ERR(db_op_copy(ocb->src_wcroot, ocb->src_relpath,
4814                      ocb->dst_wcroot, ocb->dst_relpath,
4815                      ocb->work_items, move_op_depth, scratch_pool));
4816
4817   return SVN_NO_ERROR;
4818 }
4819
4820 svn_error_t *
4821 svn_wc__db_op_copy(svn_wc__db_t *db,
4822                    const char *src_abspath,
4823                    const char *dst_abspath,
4824                    const char *dst_op_root_abspath,
4825                    svn_boolean_t is_move,
4826                    const svn_skel_t *work_items,
4827                    apr_pool_t *scratch_pool)
4828 {
4829   struct op_copy_baton ocb = {0};
4830
4831   SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
4832   SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
4833   SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_op_root_abspath));
4834
4835   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
4836                                                 &ocb.src_relpath, db,
4837                                                 src_abspath,
4838                                                 scratch_pool, scratch_pool));
4839   VERIFY_USABLE_WCROOT(ocb.src_wcroot);
4840
4841   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
4842                                                 &ocb.dst_relpath,
4843                                                 db, dst_abspath,
4844                                                 scratch_pool, scratch_pool));
4845   VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
4846
4847   ocb.work_items = work_items;
4848   ocb.is_move = is_move;
4849   ocb.dst_op_root_relpath = svn_dirent_skip_ancestor(ocb.dst_wcroot->abspath,
4850                                                      dst_op_root_abspath);
4851
4852   /* Call with the sdb in src_wcroot. It might call itself again to
4853      also obtain a lock in dst_wcroot */
4854   SVN_WC__DB_WITH_TXN(op_copy_txn(ocb.src_wcroot, &ocb, scratch_pool),
4855                       ocb.src_wcroot);
4856
4857   return SVN_NO_ERROR;
4858 }
4859
4860 /* Remove unneeded actual nodes for svn_wc__db_op_copy_layer_internal */
4861 static svn_error_t *
4862 clear_or_remove_actual(svn_wc__db_wcroot_t *wcroot,
4863                        const char *local_relpath,
4864                        int op_depth,
4865                        apr_pool_t *scratch_pool)
4866 {
4867   svn_sqlite__stmt_t *stmt;
4868   svn_boolean_t have_row, shadowed;
4869   svn_boolean_t keep_conflict = FALSE;
4870
4871   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4872                                     STMT_SELECT_NODE_INFO));
4873
4874   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4875   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4876
4877   if (have_row)
4878     {
4879       svn_wc__db_status_t presence;
4880
4881       shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth);
4882       presence = svn_sqlite__column_token(stmt, 3, presence_map);
4883
4884       if (shadowed && presence == svn_wc__db_status_base_deleted)
4885         {
4886           keep_conflict = TRUE;
4887           SVN_ERR(svn_sqlite__step(&have_row, stmt));
4888
4889           if (have_row)
4890             shadowed = (svn_sqlite__column_int(stmt, 0) > op_depth);
4891           else
4892             shadowed = FALSE;
4893         }
4894     }
4895   else
4896     shadowed = FALSE;
4897
4898   SVN_ERR(svn_sqlite__reset(stmt));
4899   if (shadowed)
4900     return SVN_NO_ERROR;
4901
4902   if (keep_conflict)
4903     {
4904       /* We don't want to accidentally remove delete-delete conflicts */
4905       SVN_ERR(svn_sqlite__get_statement(
4906                           &stmt, wcroot->sdb,
4907                           STMT_CLEAR_ACTUAL_NODE_LEAVING_CONFLICT));
4908       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4909       SVN_ERR(svn_sqlite__step_done(stmt));
4910       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4911                                         STMT_DELETE_ACTUAL_EMPTY));
4912       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4913       SVN_ERR(svn_sqlite__step_done(stmt));
4914     }
4915   else
4916     {
4917       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4918                                         STMT_DELETE_ACTUAL_NODE));
4919       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
4920       SVN_ERR(svn_sqlite__step_done(stmt));
4921     }
4922
4923   return SVN_NO_ERROR;
4924 }
4925
4926 svn_error_t *
4927 svn_wc__db_op_copy_layer_internal(svn_wc__db_wcroot_t *wcroot,
4928                                   const char *src_op_relpath,
4929                                   int src_op_depth,
4930                                   const char *dst_op_relpath,
4931                                   svn_skel_t *conflict,
4932                                   svn_skel_t *work_items,
4933                                   apr_pool_t *scratch_pool)
4934 {
4935   svn_sqlite__stmt_t *stmt, *stmt2;
4936   svn_boolean_t have_row;
4937   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
4938   int dst_op_depth = relpath_depth(dst_op_relpath);
4939   svn_boolean_t locked;
4940   svn_error_t *err = NULL;
4941
4942   SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot, dst_op_relpath,
4943                                                FALSE, scratch_pool));
4944
4945   if (!locked)
4946     return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
4947                              _("No write-lock in '%s'"),
4948                              path_for_error_message(wcroot, dst_op_relpath,
4949                                                     scratch_pool));
4950
4951   SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
4952                                     STMT_COPY_NODE_MOVE));
4953
4954   /* Replace entire subtree at one op-depth. */
4955   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
4956                                     STMT_SELECT_LAYER_FOR_REPLACE));
4957   SVN_ERR(svn_sqlite__bindf(stmt, "isdsd", wcroot->wc_id,
4958                             src_op_relpath, src_op_depth,
4959                             dst_op_relpath, dst_op_depth));
4960   SVN_ERR(svn_sqlite__step(&have_row, stmt));
4961   while (have_row)
4962     {
4963       const char *src_relpath;
4964       const char *dst_relpath;
4965
4966       svn_pool_clear(iterpool);
4967
4968       src_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
4969       dst_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
4970
4971       err = svn_sqlite__bindf(stmt2, "isdsds", wcroot->wc_id,
4972                               src_relpath, src_op_depth,
4973                               dst_relpath, dst_op_depth,
4974                               svn_relpath_dirname(dst_relpath, iterpool));
4975       if (!err)
4976         err = svn_sqlite__step_done(stmt2);
4977
4978       /* stmt2 is reset (never modified or by step_done) */
4979
4980       if (err)
4981         break;
4982
4983       /* The node can't be deleted where it is added, so extension of
4984          an existing shadowing is only interesting 2 levels deep. */
4985       if (relpath_depth(dst_relpath) > (dst_op_depth+1))
4986         {
4987           svn_boolean_t exists = !svn_sqlite__column_is_null(stmt, 3);
4988
4989           if (exists)
4990             {
4991               svn_wc__db_status_t presence;
4992
4993               presence = svn_sqlite__column_token(stmt, 3, presence_map);
4994
4995               if (presence != svn_wc__db_status_normal)
4996                 exists = FALSE;
4997             }
4998
4999           if (!exists)
5000             {
5001               svn_node_kind_t kind = svn_sqlite__column_token(stmt, 1, kind_map);
5002
5003               err = db_extend_parent_delete(wcroot, dst_relpath,
5004                                             kind, dst_op_depth, iterpool);
5005
5006               if (err)
5007                 break;
5008             }
5009         }
5010
5011       SVN_ERR(svn_sqlite__step(&have_row, stmt));
5012     }
5013
5014   SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
5015
5016   /* And now remove the records that are no longer needed */
5017   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5018                                     STMT_SELECT_NO_LONGER_MOVED_RV));
5019   SVN_ERR(svn_sqlite__bindf(stmt, "isdsd", wcroot->wc_id,
5020                             dst_op_relpath, dst_op_depth,
5021                             src_op_relpath, src_op_depth));
5022   SVN_ERR(svn_sqlite__step(&have_row, stmt));
5023   while (have_row)
5024     {
5025       const char *dst_relpath;
5026       svn_wc__db_status_t shadowed_presence;
5027
5028       svn_pool_clear(iterpool);
5029
5030       dst_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
5031
5032       if (!svn_sqlite__column_is_null(stmt, 2))
5033         shadowed_presence = svn_sqlite__column_token(stmt, 2, presence_map);
5034       else
5035         shadowed_presence = svn_wc__db_status_not_present;
5036
5037       if (shadowed_presence != svn_wc__db_status_normal
5038           && shadowed_presence != svn_wc__db_status_incomplete)
5039         {
5040           err = svn_sqlite__get_statement(&stmt2, wcroot->sdb,
5041                                             STMT_DELETE_NODE);
5042         }
5043       else
5044         {
5045           err =svn_sqlite__get_statement(&stmt2, wcroot->sdb,
5046                                          STMT_REPLACE_WITH_BASE_DELETED);
5047         }
5048
5049       if (!err)
5050         err = svn_sqlite__bindf(stmt2, "isd", wcroot->wc_id, dst_relpath,
5051                                              dst_op_depth);
5052
5053       if (!err)
5054         err = svn_sqlite__step_done(stmt2);
5055
5056       /* stmt2 is reset (never modified or by step_done) */
5057       if (err)
5058         break;
5059
5060       /* Delete ACTUAL information about this node that we just deleted */
5061       err = clear_or_remove_actual(wcroot, dst_relpath, dst_op_depth,
5062                                    scratch_pool);
5063
5064       if (err)
5065         break;
5066
5067       /* Retract base-delete for the node itself */
5068       err = db_retract_parent_delete(wcroot, dst_relpath, dst_op_depth,
5069                                      scratch_pool);
5070
5071       if (err)
5072         break;
5073
5074       SVN_ERR(svn_sqlite__step(&have_row, stmt));
5075     }
5076   svn_pool_destroy(iterpool);
5077
5078   SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
5079
5080   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5081
5082   if (conflict)
5083     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, dst_op_relpath /* ## */,
5084                                               conflict, scratch_pool));
5085
5086   return SVN_NO_ERROR;
5087 }
5088
5089 /* The txn body of svn_wc__db_op_handle_move_back */
5090 static svn_error_t *
5091 handle_move_back(svn_boolean_t *moved_back,
5092                  svn_wc__db_wcroot_t *wcroot,
5093                  const char *local_relpath,
5094                  const char *moved_from_relpath,
5095                  const svn_skel_t *work_items,
5096                  apr_pool_t *scratch_pool)
5097 {
5098   svn_sqlite__stmt_t *stmt;
5099   svn_wc__db_status_t status;
5100   svn_boolean_t op_root;
5101   svn_boolean_t have_more_work;
5102   int from_op_depth = 0;
5103   svn_boolean_t have_row;
5104   svn_boolean_t different = FALSE;
5105
5106   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5107
5108   SVN_ERR(svn_wc__db_read_info_internal(&status, NULL, NULL, NULL, NULL, NULL,
5109                                         NULL, NULL, NULL, NULL, NULL, NULL,
5110                                         NULL, NULL, NULL, NULL, NULL, NULL, NULL,
5111                                         &op_root, NULL, NULL, NULL,
5112                                         &have_more_work, NULL,
5113                                         wcroot, local_relpath,
5114                                         scratch_pool, scratch_pool));
5115
5116   if (status != svn_wc__db_status_added || !op_root)
5117     return SVN_NO_ERROR;
5118
5119   /* We have two cases here: BASE-move-back and WORKING-move-back */
5120   if (have_more_work)
5121     SVN_ERR(op_depth_of(&from_op_depth, wcroot,
5122                         svn_relpath_dirname(local_relpath, scratch_pool)));
5123   else
5124     from_op_depth = 0;
5125
5126   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5127                                     STMT_SELECT_MOVED_BACK));
5128
5129   SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id,
5130                                           local_relpath,
5131                                           from_op_depth,
5132                                           relpath_depth(local_relpath)));
5133
5134   SVN_ERR(svn_sqlite__step(&have_row, stmt));
5135
5136   SVN_ERR_ASSERT(have_row); /* We checked that the node is an op-root */
5137
5138   {
5139     svn_boolean_t moved_here = svn_sqlite__column_boolean(stmt, 9);
5140     const char *moved_to = svn_sqlite__column_text(stmt, 10, NULL);
5141
5142     if (!moved_here
5143         || !moved_to
5144         || strcmp(moved_to, moved_from_relpath))
5145       {
5146         different = TRUE;
5147         have_row = FALSE;
5148       }
5149   }
5150
5151   while (have_row)
5152     {
5153       svn_wc__db_status_t upper_status;
5154       svn_wc__db_status_t lower_status;
5155
5156       upper_status = svn_sqlite__column_token(stmt, 1, presence_map);
5157
5158       if (svn_sqlite__column_is_null(stmt, 5))
5159         {
5160           /* No lower layer replaced. */
5161           if (upper_status != svn_wc__db_status_not_present)
5162             {
5163               different = TRUE;
5164               break;
5165             }
5166           continue;
5167         }
5168
5169       lower_status = svn_sqlite__column_token(stmt, 5, presence_map);
5170
5171       if (upper_status != lower_status)
5172         {
5173           different = TRUE;
5174           break;
5175         }
5176
5177       if (upper_status == svn_wc__db_status_not_present
5178           || upper_status == svn_wc__db_status_excluded)
5179         {
5180           SVN_ERR(svn_sqlite__step(&have_row, stmt));
5181           continue; /* Nothing to check */
5182         }
5183       else if (upper_status != svn_wc__db_status_normal)
5184         {
5185           /* Not a normal move. Mixed revision move? */
5186           different = TRUE;
5187           break;
5188         }
5189
5190       {
5191         const char *upper_repos_relpath;
5192         const char *lower_repos_relpath;
5193
5194         upper_repos_relpath = svn_sqlite__column_text(stmt, 3, NULL);
5195         lower_repos_relpath = svn_sqlite__column_text(stmt, 7, NULL);
5196
5197         if (! upper_repos_relpath
5198             || strcmp(upper_repos_relpath, lower_repos_relpath))
5199           {
5200             different = TRUE;
5201             break;
5202           }
5203       }
5204
5205       {
5206         svn_revnum_t upper_rev;
5207         svn_revnum_t lower_rev;
5208
5209         upper_rev = svn_sqlite__column_revnum(stmt, 4);
5210         lower_rev = svn_sqlite__column_revnum(stmt, 8);
5211
5212         if (upper_rev != lower_rev)
5213           {
5214             different = TRUE;
5215             break;
5216           }
5217       }
5218
5219       {
5220         apr_int64_t upper_repos_id;
5221         apr_int64_t lower_repos_id;
5222
5223         upper_repos_id = svn_sqlite__column_int64(stmt, 2);
5224         lower_repos_id = svn_sqlite__column_int64(stmt, 6);
5225
5226         if (upper_repos_id != lower_repos_id)
5227           {
5228             different = TRUE;
5229             break;
5230           }
5231       }
5232
5233       /* Check moved_here? */
5234
5235       SVN_ERR(svn_sqlite__step(&have_row, stmt));
5236     }
5237   SVN_ERR(svn_sqlite__reset(stmt));
5238
5239   if (! different)
5240     {
5241       /* Ok, we can now safely remove this complete move, because we
5242          determined that it 100% matches the layer below it. */
5243
5244       /* ### We could copy the recorded timestamps from the higher to the
5245              lower layer in an attempt to improve status performance, but
5246              generally these values should be the same anyway as it was
5247              a no-op move. */
5248       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5249                                         STMT_DELETE_WORKING_OP_DEPTH));
5250
5251       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
5252                                              local_relpath,
5253                                              relpath_depth(local_relpath)));
5254
5255       SVN_ERR(svn_sqlite__step_done(stmt));
5256
5257       if (moved_back)
5258         *moved_back = TRUE;
5259     }
5260
5261   return SVN_NO_ERROR;
5262 }
5263
5264 svn_error_t *
5265 svn_wc__db_op_handle_move_back(svn_boolean_t *moved_back,
5266                                svn_wc__db_t *db,
5267                                const char *local_abspath,
5268                                const char *moved_from_abspath,
5269                                const svn_skel_t *work_items,
5270                                apr_pool_t *scratch_pool)
5271 {
5272   svn_wc__db_wcroot_t *wcroot;
5273   const char *local_relpath;
5274   const char *moved_from_relpath;
5275   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5276
5277   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5278                                                 local_abspath,
5279                                                 scratch_pool, scratch_pool));
5280   VERIFY_USABLE_WCROOT(wcroot);
5281
5282   if (moved_back)
5283     *moved_back = FALSE;
5284
5285   moved_from_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
5286                                                 moved_from_abspath);
5287
5288   if (! local_relpath[0]
5289       || !moved_from_relpath)
5290     {
5291        /* WC-Roots can't be moved */
5292       SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
5293       return SVN_NO_ERROR;
5294     }
5295
5296   SVN_WC__DB_WITH_TXN(handle_move_back(moved_back, wcroot, local_relpath,
5297                                        moved_from_relpath, work_items,
5298                                        scratch_pool),
5299                       wcroot);
5300
5301   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5302                         scratch_pool));
5303
5304   return SVN_NO_ERROR;
5305 }
5306
5307
5308 /* The recursive implementation of svn_wc__db_op_copy_shadowed_layer.
5309  *
5310  * A non-zero MOVE_OP_DEPTH implies that the copy operation is part of
5311  * a move, and indicates the op-depth of the move destination op-root. */
5312 static svn_error_t *
5313 db_op_copy_shadowed_layer(svn_wc__db_wcroot_t *src_wcroot,
5314                           const char *src_relpath,
5315                           int src_op_depth,
5316                           svn_wc__db_wcroot_t *dst_wcroot,
5317                           const char *dst_relpath,
5318                           int dst_op_depth,
5319                           int del_op_depth,
5320                           apr_int64_t repos_id,
5321                           const char *repos_relpath,
5322                           svn_revnum_t revision,
5323                           int move_op_depth,
5324                           apr_pool_t *scratch_pool)
5325 {
5326   const apr_array_header_t *children;
5327   apr_pool_t *iterpool;
5328   svn_wc__db_status_t status;
5329   svn_node_kind_t kind;
5330   svn_revnum_t node_revision;
5331   const char *node_repos_relpath;
5332   apr_int64_t node_repos_id;
5333   svn_sqlite__stmt_t *stmt;
5334   svn_wc__db_status_t dst_presence;
5335   int i;
5336
5337   {
5338     svn_error_t *err;
5339     err = svn_wc__db_depth_get_info(&status, &kind, &node_revision,
5340                                     &node_repos_relpath, &node_repos_id,
5341                                     NULL, NULL, NULL, NULL, NULL, NULL,
5342                                     NULL, NULL,
5343                                     src_wcroot, src_relpath, src_op_depth,
5344                                     scratch_pool, scratch_pool);
5345
5346     if (err)
5347       {
5348         if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
5349           return svn_error_trace(err);
5350
5351         svn_error_clear(err);
5352         return SVN_NO_ERROR; /* There is no shadowed node at src_op_depth */
5353       }
5354   }
5355
5356   if (src_op_depth == 0)
5357     {
5358       /* If the node is switched or has a different revision then its parent
5359          we shouldn't copy it. (We can't as we would have to insert it at
5360          an unshadowed depth) */
5361       if (status == svn_wc__db_status_not_present
5362           || status == svn_wc__db_status_excluded
5363           || status == svn_wc__db_status_server_excluded
5364           || node_revision != revision
5365           || node_repos_id != repos_id
5366           || strcmp(node_repos_relpath, repos_relpath))
5367         {
5368           /* Add a not-present node in the destination wcroot */
5369           struct insert_working_baton_t iwb;
5370           const char *repos_root_url;
5371           const char *repos_uuid;
5372
5373           SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, &repos_uuid,
5374                                               src_wcroot, node_repos_id,
5375                                               scratch_pool));
5376
5377           SVN_ERR(create_repos_id(&node_repos_id, repos_root_url, repos_uuid,
5378                                   dst_wcroot->sdb, scratch_pool));
5379
5380           blank_iwb(&iwb);
5381
5382           iwb.op_depth = dst_op_depth;
5383           if (status != svn_wc__db_status_excluded)
5384             iwb.presence = svn_wc__db_status_not_present;
5385           else
5386             iwb.presence = svn_wc__db_status_excluded;
5387
5388           iwb.kind = kind;
5389
5390           iwb.original_repos_id = node_repos_id;
5391           iwb.original_revnum = node_revision;
5392           iwb.original_repos_relpath = node_repos_relpath;
5393
5394           SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5395                                       scratch_pool));
5396
5397           return SVN_NO_ERROR;
5398         }
5399     }
5400
5401   iterpool = svn_pool_create(scratch_pool);
5402
5403   switch (status)
5404     {
5405     case svn_wc__db_status_normal:
5406     case svn_wc__db_status_added:
5407     case svn_wc__db_status_moved_here:
5408     case svn_wc__db_status_copied:
5409       dst_presence = svn_wc__db_status_normal;
5410       break;
5411     case svn_wc__db_status_deleted:
5412     case svn_wc__db_status_not_present:
5413       dst_presence = svn_wc__db_status_not_present;
5414       break;
5415     case svn_wc__db_status_excluded:
5416       dst_presence = svn_wc__db_status_excluded;
5417       break;
5418     case svn_wc__db_status_server_excluded:
5419       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5420                                _("Cannot copy '%s' excluded by server"),
5421                                path_for_error_message(src_wcroot,
5422                                                       src_relpath,
5423                                                       scratch_pool));
5424     default:
5425       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
5426                                _("Cannot handle status of '%s'"),
5427                                path_for_error_message(src_wcroot,
5428                                                       src_relpath,
5429                                                       scratch_pool));
5430     }
5431
5432   if (dst_presence == svn_wc__db_status_normal
5433       && src_wcroot == dst_wcroot) /* ### Remove limitation */
5434     {
5435       SVN_ERR(svn_sqlite__get_statement(&stmt, src_wcroot->sdb,
5436                              STMT_INSERT_WORKING_NODE_COPY_FROM_DEPTH));
5437
5438       SVN_ERR(svn_sqlite__bindf(stmt, "issdstd",
5439                         src_wcroot->wc_id, src_relpath,
5440                         dst_relpath,
5441                         dst_op_depth,
5442                         svn_relpath_dirname(dst_relpath, iterpool),
5443                         presence_map, dst_presence,
5444                         src_op_depth));
5445
5446       /* moved_here */
5447       if (dst_op_depth == move_op_depth)
5448         SVN_ERR(svn_sqlite__bind_int(stmt, 8, TRUE));
5449
5450       SVN_ERR(svn_sqlite__step_done(stmt));
5451
5452       {
5453         /* And mark it deleted to allow proper shadowing */
5454         struct insert_working_baton_t iwb;
5455
5456         blank_iwb(&iwb);
5457
5458         iwb.op_depth = del_op_depth;
5459         iwb.presence = svn_wc__db_status_base_deleted;
5460
5461         iwb.kind = kind;
5462
5463         SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5464                                     scratch_pool));
5465       }
5466     }
5467   else
5468     {
5469       struct insert_working_baton_t iwb;
5470       if (dst_presence == svn_wc__db_status_normal) /* Fallback for multi-db */
5471         dst_presence = svn_wc__db_status_not_present;
5472
5473       /* And mark it deleted to allow proper shadowing */
5474
5475       blank_iwb(&iwb);
5476
5477       iwb.op_depth = dst_op_depth;
5478       iwb.presence = dst_presence;
5479       iwb.kind = kind;
5480
5481       SVN_ERR(insert_working_node(&iwb, dst_wcroot, dst_relpath,
5482                                     scratch_pool));
5483     }
5484
5485   if (dst_presence == svn_wc__db_status_not_present)
5486     {
5487       /* Don't create descendants of a not present node! */
5488
5489       /* This code is currently still triggered by copying deleted nodes
5490          between separate working copies. See ### comment above. */
5491
5492       svn_pool_destroy(iterpool);
5493       return SVN_NO_ERROR;
5494     }
5495
5496   SVN_ERR(gather_children(&children, src_wcroot, src_relpath,
5497                           STMT_SELECT_OP_DEPTH_CHILDREN, src_op_depth,
5498                           scratch_pool, iterpool));
5499
5500   for (i = 0; i < children->nelts; i++)
5501     {
5502       const char *name = APR_ARRAY_IDX(children, i, const char *);
5503       const char *child_src_relpath;
5504       const char *child_dst_relpath;
5505       const char *child_repos_relpath = NULL;
5506
5507       svn_pool_clear(iterpool);
5508       child_src_relpath = svn_relpath_join(src_relpath, name, iterpool);
5509       child_dst_relpath = svn_relpath_join(dst_relpath, name, iterpool);
5510
5511       if (repos_relpath)
5512         child_repos_relpath = svn_relpath_join(repos_relpath, name, iterpool);
5513
5514       SVN_ERR(db_op_copy_shadowed_layer(
5515                          src_wcroot, child_src_relpath, src_op_depth,
5516                          dst_wcroot, child_dst_relpath, dst_op_depth,
5517                          del_op_depth,
5518                          repos_id, child_repos_relpath, revision,
5519                          move_op_depth, scratch_pool));
5520     }
5521
5522   svn_pool_destroy(iterpool);
5523
5524   return SVN_NO_ERROR;
5525 }
5526
5527 /* Helper for svn_wc__db_op_copy_shadowed_layer(). */
5528 static svn_error_t *
5529 op_copy_shadowed_layer_txn(svn_wc__db_wcroot_t *wcroot,
5530                            struct op_copy_baton *ocb,
5531                            apr_pool_t *scratch_pool)
5532 {
5533   const char *src_parent_relpath;
5534   const char *dst_parent_relpath;
5535   int src_op_depth;
5536   int dst_op_depth;
5537   int del_op_depth;
5538   const char *repos_relpath = NULL;
5539   apr_int64_t repos_id = INVALID_REPOS_ID;
5540   svn_revnum_t revision = SVN_INVALID_REVNUM;
5541
5542   if (wcroot != ocb->dst_wcroot)
5543     {
5544       /* Source and destination databases differ; so also start a lock
5545          in the destination database, by calling ourself in an extra lock. */
5546
5547       SVN_WC__DB_WITH_TXN(op_copy_shadowed_layer_txn(ocb->dst_wcroot, ocb,
5548                                                      scratch_pool),
5549                           ocb->dst_wcroot);
5550
5551       return SVN_NO_ERROR;
5552     }
5553
5554   /* From this point we can assume a lock in the src and dst databases */
5555
5556
5557   /* src_relpath and dst_relpath can't be wcroot as we need their parents */
5558   SVN_ERR_ASSERT(*ocb->src_relpath && *ocb->dst_relpath);
5559
5560   src_parent_relpath = svn_relpath_dirname(ocb->src_relpath, scratch_pool);
5561   dst_parent_relpath = svn_relpath_dirname(ocb->dst_relpath, scratch_pool);
5562
5563   /* src_parent must be status normal or added; get its op-depth */
5564   SVN_ERR(op_depth_of(&src_op_depth, ocb->src_wcroot, src_parent_relpath));
5565
5566   /* dst_parent must be status added; get its op-depth */
5567   SVN_ERR(op_depth_of(&dst_op_depth, ocb->dst_wcroot, dst_parent_relpath));
5568
5569   del_op_depth = relpath_depth(ocb->dst_relpath);
5570
5571   /* Get some information from the parent */
5572   SVN_ERR(svn_wc__db_depth_get_info(NULL, NULL, &revision, &repos_relpath,
5573                                     &repos_id, NULL, NULL, NULL, NULL, NULL,
5574                                     NULL, NULL, NULL,
5575                                     ocb->src_wcroot,
5576                                     src_parent_relpath, src_op_depth,
5577                                     scratch_pool, scratch_pool));
5578
5579   if (repos_relpath == NULL)
5580     {
5581       /* The node is a local addition and has no shadowed information */
5582       return SVN_NO_ERROR;
5583     }
5584
5585   /* And calculate the child repos relpath */
5586   repos_relpath = svn_relpath_join(repos_relpath,
5587                                    svn_relpath_basename(ocb->src_relpath,
5588                                                         NULL),
5589                                    scratch_pool);
5590
5591   SVN_ERR(db_op_copy_shadowed_layer(
5592                         ocb->src_wcroot, ocb->src_relpath, src_op_depth,
5593                         ocb->dst_wcroot, ocb->dst_relpath, dst_op_depth,
5594                         del_op_depth,
5595                         repos_id, repos_relpath, revision,
5596                         (ocb->is_move ? dst_op_depth : 0),
5597                         scratch_pool));
5598
5599   return SVN_NO_ERROR;
5600 }
5601
5602 svn_error_t *
5603 svn_wc__db_op_copy_shadowed_layer(svn_wc__db_t *db,
5604                                   const char *src_abspath,
5605                                   const char *dst_abspath,
5606                                   svn_boolean_t is_move,
5607                                   apr_pool_t *scratch_pool)
5608 {
5609   struct op_copy_baton ocb = {0};
5610
5611   SVN_ERR_ASSERT(svn_dirent_is_absolute(src_abspath));
5612   SVN_ERR_ASSERT(svn_dirent_is_absolute(dst_abspath));
5613
5614   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.src_wcroot,
5615                                                 &ocb.src_relpath, db,
5616                                                 src_abspath,
5617                                                 scratch_pool, scratch_pool));
5618   VERIFY_USABLE_WCROOT(ocb.src_wcroot);
5619
5620   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&ocb.dst_wcroot,
5621                                                 &ocb.dst_relpath,
5622                                                 db, dst_abspath,
5623                                                 scratch_pool, scratch_pool));
5624   VERIFY_USABLE_WCROOT(ocb.dst_wcroot);
5625
5626   ocb.is_move = is_move;
5627   ocb.dst_op_root_relpath = NULL; /* not used by op_copy_shadowed_layer_txn */
5628
5629   ocb.work_items = NULL;
5630
5631   /* Call with the sdb in src_wcroot. It might call itself again to
5632      also obtain a lock in dst_wcroot */
5633   SVN_WC__DB_WITH_TXN(op_copy_shadowed_layer_txn(ocb.src_wcroot, &ocb,
5634                                                  scratch_pool),
5635                       ocb.src_wcroot);
5636
5637   return SVN_NO_ERROR;
5638 }
5639
5640
5641 /* If there are any server-excluded base nodes then the copy must fail
5642    as it's not possible to commit such a copy.
5643    Return an error if there are any server-excluded nodes. */
5644 static svn_error_t *
5645 catch_copy_of_server_excluded(svn_wc__db_wcroot_t *wcroot,
5646                               const char *local_relpath,
5647                               apr_pool_t *scratch_pool)
5648 {
5649   svn_sqlite__stmt_t *stmt;
5650   svn_boolean_t have_row;
5651   const char *server_excluded_relpath;
5652
5653   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
5654                                     STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
5655   SVN_ERR(svn_sqlite__bindf(stmt, "is",
5656                             wcroot->wc_id,
5657                             local_relpath));
5658   SVN_ERR(svn_sqlite__step(&have_row, stmt));
5659   if (have_row)
5660     server_excluded_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
5661   SVN_ERR(svn_sqlite__reset(stmt));
5662   if (have_row)
5663     return svn_error_createf(SVN_ERR_AUTHZ_UNREADABLE, NULL,
5664                              _("Cannot copy '%s' excluded by server"),
5665                              path_for_error_message(wcroot,
5666                                                     server_excluded_relpath,
5667                                                     scratch_pool));
5668
5669   return SVN_NO_ERROR;
5670 }
5671
5672
5673 svn_error_t *
5674 svn_wc__db_op_copy_dir(svn_wc__db_t *db,
5675                        const char *local_abspath,
5676                        const apr_hash_t *props,
5677                        svn_revnum_t changed_rev,
5678                        apr_time_t changed_date,
5679                        const char *changed_author,
5680                        const char *original_repos_relpath,
5681                        const char *original_root_url,
5682                        const char *original_uuid,
5683                        svn_revnum_t original_revision,
5684                        const apr_array_header_t *children,
5685                        svn_depth_t depth,
5686                        svn_boolean_t is_move,
5687                        const svn_skel_t *conflict,
5688                        const svn_skel_t *work_items,
5689                        apr_pool_t *scratch_pool)
5690 {
5691   svn_wc__db_wcroot_t *wcroot;
5692   const char *local_relpath;
5693   insert_working_baton_t iwb;
5694   int parent_op_depth;
5695
5696   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5697   SVN_ERR_ASSERT(props != NULL);
5698   /* ### any assertions for CHANGED_* ?  */
5699   /* ### any assertions for ORIGINAL_* ?  */
5700 #if 0
5701   SVN_ERR_ASSERT(children != NULL);
5702 #endif
5703
5704   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5705                               local_abspath, scratch_pool, scratch_pool));
5706   VERIFY_USABLE_WCROOT(wcroot);
5707
5708   blank_iwb(&iwb);
5709
5710   iwb.presence = svn_wc__db_status_normal;
5711   iwb.kind = svn_node_dir;
5712
5713   if (original_root_url != NULL)
5714     {
5715       SVN_ERR(create_repos_id(&iwb.original_repos_id,
5716                               original_root_url, original_uuid,
5717                               wcroot->sdb, scratch_pool));
5718       iwb.original_repos_relpath = original_repos_relpath;
5719       iwb.original_revnum = original_revision;
5720
5721       iwb.props = props;
5722       iwb.changed_rev = changed_rev;
5723       iwb.changed_date = changed_date;
5724       iwb.changed_author = changed_author;
5725     }
5726
5727   /* ### Should we do this inside the transaction? */
5728   SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5729                             &parent_op_depth, iwb.original_repos_id,
5730                             original_repos_relpath, original_revision,
5731                             wcroot, local_relpath, scratch_pool));
5732
5733   iwb.children = children;
5734   iwb.depth = depth;
5735   iwb.moved_here = is_move && (parent_op_depth == 0 ||
5736                                iwb.op_depth == parent_op_depth);
5737
5738   iwb.work_items = work_items;
5739   iwb.conflict = conflict;
5740
5741   SVN_WC__DB_WITH_TXN(
5742                 insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5743                 wcroot);
5744   SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
5745
5746   return SVN_NO_ERROR;
5747 }
5748
5749
5750 svn_error_t *
5751 svn_wc__db_op_copy_file(svn_wc__db_t *db,
5752                         const char *local_abspath,
5753                         const apr_hash_t *props,
5754                         svn_revnum_t changed_rev,
5755                         apr_time_t changed_date,
5756                         const char *changed_author,
5757                         const char *original_repos_relpath,
5758                         const char *original_root_url,
5759                         const char *original_uuid,
5760                         svn_revnum_t original_revision,
5761                         const svn_checksum_t *checksum,
5762                         svn_boolean_t update_actual_props,
5763                         const apr_hash_t *new_actual_props,
5764                         svn_boolean_t is_move,
5765                         const svn_skel_t *conflict,
5766                         const svn_skel_t *work_items,
5767                         apr_pool_t *scratch_pool)
5768 {
5769   svn_wc__db_wcroot_t *wcroot;
5770   const char *local_relpath;
5771   insert_working_baton_t iwb;
5772   int parent_op_depth;
5773
5774   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5775   SVN_ERR_ASSERT(props != NULL);
5776   /* ### any assertions for CHANGED_* ?  */
5777   SVN_ERR_ASSERT((! original_repos_relpath && ! original_root_url
5778                   && ! original_uuid && ! checksum
5779                   && original_revision == SVN_INVALID_REVNUM)
5780                  || (original_repos_relpath && original_root_url
5781                      && original_uuid && checksum
5782                      && original_revision != SVN_INVALID_REVNUM));
5783
5784   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5785                               local_abspath, scratch_pool, scratch_pool));
5786   VERIFY_USABLE_WCROOT(wcroot);
5787
5788   blank_iwb(&iwb);
5789
5790   iwb.presence = svn_wc__db_status_normal;
5791   iwb.kind = svn_node_file;
5792
5793   if (original_root_url != NULL)
5794     {
5795       SVN_ERR(create_repos_id(&iwb.original_repos_id,
5796                               original_root_url, original_uuid,
5797                               wcroot->sdb, scratch_pool));
5798       iwb.original_repos_relpath = original_repos_relpath;
5799       iwb.original_revnum = original_revision;
5800
5801       iwb.props = props;
5802       iwb.changed_rev = changed_rev;
5803       iwb.changed_date = changed_date;
5804       iwb.changed_author = changed_author;
5805     }
5806
5807   /* ### Should we do this inside the transaction? */
5808   SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5809                             &parent_op_depth, iwb.original_repos_id,
5810                             original_repos_relpath, original_revision,
5811                             wcroot, local_relpath, scratch_pool));
5812
5813   iwb.checksum = checksum;
5814   iwb.moved_here = is_move && (parent_op_depth == 0 ||
5815                                iwb.op_depth == parent_op_depth);
5816
5817   if (update_actual_props)
5818     {
5819       iwb.update_actual_props = update_actual_props;
5820       iwb.new_actual_props = new_actual_props;
5821     }
5822
5823   iwb.work_items = work_items;
5824   iwb.conflict = conflict;
5825
5826   SVN_WC__DB_WITH_TXN(
5827           insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5828           wcroot);
5829   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5830
5831   return SVN_NO_ERROR;
5832 }
5833
5834
5835 svn_error_t *
5836 svn_wc__db_op_copy_symlink(svn_wc__db_t *db,
5837                            const char *local_abspath,
5838                            const apr_hash_t *props,
5839                            svn_revnum_t changed_rev,
5840                            apr_time_t changed_date,
5841                            const char *changed_author,
5842                            const char *original_repos_relpath,
5843                            const char *original_root_url,
5844                            const char *original_uuid,
5845                            svn_revnum_t original_revision,
5846                            const char *target,
5847                            svn_boolean_t is_move,
5848                            const svn_skel_t *conflict,
5849                            const svn_skel_t *work_items,
5850                            apr_pool_t *scratch_pool)
5851 {
5852   svn_wc__db_wcroot_t *wcroot;
5853   const char *local_relpath;
5854   insert_working_baton_t iwb;
5855   int parent_op_depth;
5856
5857   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5858   SVN_ERR_ASSERT(props != NULL);
5859   /* ### any assertions for CHANGED_* ?  */
5860   /* ### any assertions for ORIGINAL_* ?  */
5861   SVN_ERR_ASSERT(target != NULL);
5862
5863   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5864                               local_abspath, scratch_pool, scratch_pool));
5865   VERIFY_USABLE_WCROOT(wcroot);
5866
5867   blank_iwb(&iwb);
5868
5869   iwb.presence = svn_wc__db_status_normal;
5870   iwb.kind = svn_node_symlink;
5871
5872
5873   if (original_root_url != NULL)
5874     {
5875       SVN_ERR(create_repos_id(&iwb.original_repos_id,
5876                               original_root_url, original_uuid,
5877                               wcroot->sdb, scratch_pool));
5878       iwb.original_repos_relpath = original_repos_relpath;
5879       iwb.original_revnum = original_revision;
5880
5881       iwb.props = props;
5882       iwb.changed_rev = changed_rev;
5883       iwb.changed_date = changed_date;
5884       iwb.changed_author = changed_author;
5885     }
5886
5887   /* ### Should we do this inside the transaction? */
5888   SVN_ERR(op_depth_for_copy(&iwb.op_depth, &iwb.not_present_op_depth,
5889                             &parent_op_depth, iwb.original_repos_id,
5890                             original_repos_relpath, original_revision,
5891                             wcroot, local_relpath, scratch_pool));
5892
5893   iwb.target = target;
5894   iwb.moved_here = is_move && (parent_op_depth == 0 ||
5895                                iwb.op_depth == parent_op_depth);
5896
5897   iwb.work_items = work_items;
5898   iwb.conflict = conflict;
5899
5900   SVN_WC__DB_WITH_TXN(
5901             insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5902             wcroot);
5903   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5904
5905   return SVN_NO_ERROR;
5906 }
5907
5908
5909 svn_error_t *
5910 svn_wc__db_op_add_directory(svn_wc__db_t *db,
5911                             const char *local_abspath,
5912                             const apr_hash_t *props,
5913                             const svn_skel_t *work_items,
5914                             apr_pool_t *scratch_pool)
5915 {
5916   svn_wc__db_wcroot_t *wcroot;
5917   const char *local_relpath;
5918   const char *dir_abspath;
5919   const char *name;
5920   insert_working_baton_t iwb;
5921
5922   /* Resolve wcroot via parent directory to avoid obstruction handling */
5923   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5924   svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5925
5926   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5927                               dir_abspath, scratch_pool, scratch_pool));
5928   VERIFY_USABLE_WCROOT(wcroot);
5929
5930   blank_iwb(&iwb);
5931
5932   local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5933   iwb.presence = svn_wc__db_status_normal;
5934   iwb.kind = svn_node_dir;
5935   iwb.op_depth = relpath_depth(local_relpath);
5936   if (props && apr_hash_count((apr_hash_t *)props))
5937     {
5938       iwb.update_actual_props = TRUE;
5939       iwb.new_actual_props = props;
5940     }
5941
5942   iwb.work_items = work_items;
5943
5944   SVN_WC__DB_WITH_TXN(
5945             insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5946             wcroot);
5947   /* Use depth infinity to make sure we have no invalid cached information
5948    * about children of this dir. */
5949   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
5950                         scratch_pool));
5951
5952   return SVN_NO_ERROR;
5953 }
5954
5955
5956 svn_error_t *
5957 svn_wc__db_op_add_file(svn_wc__db_t *db,
5958                        const char *local_abspath,
5959                        const apr_hash_t *props,
5960                        const svn_skel_t *work_items,
5961                        apr_pool_t *scratch_pool)
5962 {
5963   svn_wc__db_wcroot_t *wcroot;
5964   const char *local_relpath;
5965   insert_working_baton_t iwb;
5966   const char *dir_abspath;
5967   const char *name;
5968
5969   /* Resolve wcroot via parent directory to avoid obstruction handling */
5970   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
5971   svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
5972
5973   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
5974                               dir_abspath, scratch_pool, scratch_pool));
5975   VERIFY_USABLE_WCROOT(wcroot);
5976
5977   blank_iwb(&iwb);
5978
5979   local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
5980   iwb.presence = svn_wc__db_status_normal;
5981   iwb.kind = svn_node_file;
5982   iwb.op_depth = relpath_depth(local_relpath);
5983   if (props && apr_hash_count((apr_hash_t *)props))
5984     {
5985       iwb.update_actual_props = TRUE;
5986       iwb.new_actual_props = props;
5987     }
5988
5989   iwb.work_items = work_items;
5990
5991   SVN_WC__DB_WITH_TXN(
5992             insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
5993             wcroot);
5994   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
5995
5996   return SVN_NO_ERROR;
5997 }
5998
5999
6000 svn_error_t *
6001 svn_wc__db_op_add_symlink(svn_wc__db_t *db,
6002                           const char *local_abspath,
6003                           const char *target,
6004                           const apr_hash_t *props,
6005                           const svn_skel_t *work_items,
6006                           apr_pool_t *scratch_pool)
6007 {
6008   svn_wc__db_wcroot_t *wcroot;
6009   const char *local_relpath;
6010   insert_working_baton_t iwb;
6011   const char *dir_abspath;
6012   const char *name;
6013
6014   /* Resolve wcroot via parent directory to avoid obstruction handling */
6015   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6016   SVN_ERR_ASSERT(target != NULL);
6017
6018   svn_dirent_split(&dir_abspath, &name, local_abspath, scratch_pool);
6019
6020   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6021                               dir_abspath, scratch_pool, scratch_pool));
6022
6023   VERIFY_USABLE_WCROOT(wcroot);
6024
6025   blank_iwb(&iwb);
6026
6027   local_relpath = svn_relpath_join(local_relpath, name, scratch_pool);
6028   iwb.presence = svn_wc__db_status_normal;
6029   iwb.kind = svn_node_symlink;
6030   iwb.op_depth = relpath_depth(local_relpath);
6031   if (props && apr_hash_count((apr_hash_t *)props))
6032     {
6033       iwb.update_actual_props = TRUE;
6034       iwb.new_actual_props = props;
6035     }
6036
6037   iwb.target = target;
6038
6039   iwb.work_items = work_items;
6040
6041   SVN_WC__DB_WITH_TXN(
6042             insert_working_node(&iwb, wcroot, local_relpath, scratch_pool),
6043             wcroot);
6044   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6045
6046   return SVN_NO_ERROR;
6047 }
6048
6049 /* Record RECORDED_SIZE and RECORDED_TIME into top layer in NODES */
6050 static svn_error_t *
6051 db_record_fileinfo(svn_wc__db_wcroot_t *wcroot,
6052                    const char *local_relpath,
6053                    apr_int64_t recorded_size,
6054                    apr_int64_t recorded_time,
6055                    apr_pool_t *scratch_pool)
6056 {
6057   svn_sqlite__stmt_t *stmt;
6058   int affected_rows;
6059
6060   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6061                                     STMT_UPDATE_NODE_FILEINFO));
6062   SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
6063                             recorded_size, recorded_time));
6064   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6065
6066   SVN_ERR_ASSERT(affected_rows == 1);
6067
6068   return SVN_NO_ERROR;
6069 }
6070
6071
6072 svn_error_t *
6073 svn_wc__db_global_record_fileinfo(svn_wc__db_t *db,
6074                                   const char *local_abspath,
6075                                   svn_filesize_t recorded_size,
6076                                   apr_time_t recorded_time,
6077                                   apr_pool_t *scratch_pool)
6078 {
6079   svn_wc__db_wcroot_t *wcroot;
6080   const char *local_relpath;
6081
6082   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6083
6084   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6085                               local_abspath, scratch_pool, scratch_pool));
6086   VERIFY_USABLE_WCROOT(wcroot);
6087
6088   SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
6089                              recorded_size, recorded_time, scratch_pool));
6090
6091   /* We *totally* monkeyed the entries. Toss 'em.  */
6092   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6093
6094   return SVN_NO_ERROR;
6095 }
6096
6097
6098 /* Set the ACTUAL_NODE properties column for (WC_ID, LOCAL_RELPATH) to
6099  * PROPS.
6100  *
6101  * Note: PROPS=NULL means the actual props are the same as the pristine
6102  * props; to indicate no properties when the pristine has some props,
6103  * PROPS must be an empty hash. */
6104 static svn_error_t *
6105 set_actual_props(svn_wc__db_wcroot_t *wcroot,
6106                  const char *local_relpath,
6107                  apr_hash_t *props,
6108                  apr_pool_t *scratch_pool)
6109 {
6110   svn_sqlite__stmt_t *stmt;
6111   int affected_rows;
6112
6113   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6114                                     STMT_UPDATE_ACTUAL_PROPS));
6115   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6116   SVN_ERR(svn_sqlite__bind_properties(stmt, 3, props, scratch_pool));
6117   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6118
6119   if (affected_rows == 1 || !props)
6120     {
6121       /* Perhaps the entire ACTUAL record is unneeded now? */
6122       if (!props && affected_rows)
6123         {
6124           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6125                                             STMT_DELETE_ACTUAL_EMPTY));
6126           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6127           SVN_ERR(svn_sqlite__step_done(stmt));
6128         }
6129
6130       return SVN_NO_ERROR; /* We are done */
6131     }
6132
6133   /* We have to insert a row in ACTUAL */
6134
6135   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6136                                     STMT_INSERT_ACTUAL_PROPS));
6137   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6138   if (*local_relpath != '\0')
6139     SVN_ERR(svn_sqlite__bind_text(stmt, 3,
6140                                   svn_relpath_dirname(local_relpath,
6141                                                       scratch_pool)));
6142   SVN_ERR(svn_sqlite__bind_properties(stmt, 4, props, scratch_pool));
6143   return svn_error_trace(svn_sqlite__step_done(stmt));
6144 }
6145
6146 svn_error_t *
6147 svn_wc__db_op_set_props_internal(svn_wc__db_wcroot_t *wcroot,
6148                                  const char *local_relpath,
6149                                  apr_hash_t *props,
6150                                  svn_boolean_t clear_recorded_info,
6151                                  apr_pool_t *scratch_pool)
6152 {
6153   SVN_ERR(set_actual_props(wcroot, local_relpath, props, scratch_pool));
6154
6155   if (clear_recorded_info)
6156     {
6157       SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
6158                                  SVN_INVALID_FILESIZE, 0,
6159                                  scratch_pool));
6160     }
6161
6162   return SVN_NO_ERROR;
6163 }
6164
6165 /* The body of svn_wc__db_op_set_props().
6166
6167    Set the 'properties' column in the 'ACTUAL_NODE' table to BATON->props.
6168    Create an entry in the ACTUAL table for the node if it does not yet
6169    have one.
6170    To specify no properties, BATON->props must be an empty hash, not NULL.
6171    BATON is of type 'struct set_props_baton_t'.
6172 */
6173 static svn_error_t *
6174 set_props_txn(svn_wc__db_wcroot_t *wcroot,
6175               const char *local_relpath,
6176               apr_hash_t *props,
6177               svn_boolean_t clear_recorded_info,
6178               const svn_skel_t *conflict,
6179               const svn_skel_t *work_items,
6180               apr_pool_t *scratch_pool)
6181 {
6182   apr_hash_t *pristine_props;
6183
6184   /* Check if the props are modified. If no changes, then wipe out the
6185      ACTUAL props.  PRISTINE_PROPS==NULL means that any
6186      ACTUAL props are okay as provided, so go ahead and set them.  */
6187   SVN_ERR(db_read_pristine_props(&pristine_props, wcroot, local_relpath, FALSE,
6188                                  scratch_pool, scratch_pool));
6189   if (props && pristine_props)
6190     {
6191       apr_array_header_t *prop_diffs;
6192
6193       SVN_ERR(svn_prop_diffs(&prop_diffs, props, pristine_props,
6194                              scratch_pool));
6195       if (prop_diffs->nelts == 0)
6196         props = NULL;
6197     }
6198
6199   SVN_ERR(svn_wc__db_op_set_props_internal(wcroot, local_relpath, props,
6200                                            clear_recorded_info, scratch_pool));
6201
6202   /* And finally.  */
6203   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6204   if (conflict)
6205     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6206                                               conflict, scratch_pool));
6207
6208   return SVN_NO_ERROR;
6209 }
6210
6211
6212 svn_error_t *
6213 svn_wc__db_op_set_props(svn_wc__db_t *db,
6214                         const char *local_abspath,
6215                         apr_hash_t *props,
6216                         svn_boolean_t clear_recorded_info,
6217                         const svn_skel_t *conflict,
6218                         const svn_skel_t *work_items,
6219                         apr_pool_t *scratch_pool)
6220 {
6221   svn_wc__db_wcroot_t *wcroot;
6222   const char *local_relpath;
6223
6224   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6225
6226   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6227                               db, local_abspath, scratch_pool, scratch_pool));
6228   VERIFY_USABLE_WCROOT(wcroot);
6229
6230   SVN_WC__DB_WITH_TXN(set_props_txn(wcroot, local_relpath, props,
6231                                     clear_recorded_info, conflict, work_items,
6232                                     scratch_pool),
6233                       wcroot);
6234   return SVN_NO_ERROR;
6235 }
6236
6237
6238 svn_error_t *
6239 svn_wc__db_op_modified(svn_wc__db_t *db,
6240                        const char *local_abspath,
6241                        apr_pool_t *scratch_pool)
6242 {
6243   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6244
6245   NOT_IMPLEMENTED();
6246 }
6247
6248 /* */
6249 static svn_error_t *
6250 populate_targets_tree(svn_wc__db_wcroot_t *wcroot,
6251                       const char *local_relpath,
6252                       svn_depth_t depth,
6253                       const apr_array_header_t *changelist_filter,
6254                       apr_pool_t *scratch_pool)
6255 {
6256   svn_sqlite__stmt_t *stmt;
6257   int affected_rows = 0;
6258   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6259                                       STMT_CREATE_TARGETS_LIST));
6260
6261   if (changelist_filter && changelist_filter->nelts > 0)
6262     {
6263       /* Iterate over the changelists, adding the nodes which match.
6264          Common case: we only have one changelist, so this only
6265          happens once. */
6266       int i;
6267       int stmt_idx;
6268
6269       switch (depth)
6270         {
6271           case svn_depth_empty:
6272             stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST;
6273             break;
6274
6275           case svn_depth_files:
6276             stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_FILES;
6277             break;
6278
6279           case svn_depth_immediates:
6280             stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_IMMEDIATES;
6281             break;
6282
6283           case svn_depth_infinity:
6284             stmt_idx = STMT_INSERT_TARGET_WITH_CHANGELIST_DEPTH_INFINITY;
6285             break;
6286
6287           default:
6288             /* We don't know how to handle unknown or exclude. */
6289             SVN_ERR_MALFUNCTION();
6290             break;
6291         }
6292
6293       for (i = 0; i < changelist_filter->nelts; i++)
6294         {
6295           int sub_affected;
6296           const char *changelist = APR_ARRAY_IDX(changelist_filter, i,
6297                                                  const char *);
6298
6299           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6300                                         STMT_INSERT_TARGET_WITH_CHANGELIST));
6301           SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
6302                                     local_relpath, changelist));
6303           SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6304
6305           /* If the root is matched by the changelist, we don't have to match
6306              the children. As that tells us the root is a file */
6307           if (!sub_affected && depth > svn_depth_empty)
6308             {
6309               SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
6310               SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
6311                                         local_relpath, changelist));
6312               SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6313             }
6314
6315           affected_rows += sub_affected;
6316         }
6317     }
6318   else /* No changelist filtering */
6319     {
6320       int stmt_idx;
6321       int sub_affected;
6322
6323       switch (depth)
6324         {
6325           case svn_depth_empty:
6326             stmt_idx = STMT_INSERT_TARGET;
6327             break;
6328
6329           case svn_depth_files:
6330             stmt_idx = STMT_INSERT_TARGET_DEPTH_FILES;
6331             break;
6332
6333           case svn_depth_immediates:
6334             stmt_idx = STMT_INSERT_TARGET_DEPTH_IMMEDIATES;
6335             break;
6336
6337           case svn_depth_infinity:
6338             stmt_idx = STMT_INSERT_TARGET_DEPTH_INFINITY;
6339             break;
6340
6341           default:
6342             /* We don't know how to handle unknown or exclude. */
6343             SVN_ERR_MALFUNCTION();
6344             break;
6345         }
6346
6347       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6348                                         STMT_INSERT_TARGET));
6349       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6350       SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6351       affected_rows += sub_affected;
6352
6353       if (depth > svn_depth_empty)
6354         {
6355           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
6356           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6357           SVN_ERR(svn_sqlite__update(&sub_affected, stmt));
6358           affected_rows += sub_affected;
6359         }
6360     }
6361
6362   /* Does the target exist? */
6363   if (affected_rows == 0)
6364     {
6365       svn_boolean_t exists;
6366       SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
6367
6368       if (!exists)
6369         return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6370                                  _("The node '%s' was not found."),
6371                                  path_for_error_message(wcroot,
6372                                                         local_relpath,
6373                                                         scratch_pool));
6374     }
6375
6376   return SVN_NO_ERROR;
6377 }
6378
6379
6380 #if 0
6381 static svn_error_t *
6382 dump_targets(svn_wc__db_wcroot_t *wcroot,
6383              apr_pool_t *scratch_pool)
6384 {
6385   svn_sqlite__stmt_t *stmt;
6386   svn_boolean_t have_row;
6387
6388   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6389                                     STMT_SELECT_TARGETS));
6390   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6391   while (have_row)
6392     {
6393       const char *target = svn_sqlite__column_text(stmt, 0, NULL);
6394       SVN_DBG(("Target: '%s'\n", target));
6395       SVN_ERR(svn_sqlite__step(&have_row, stmt));
6396     }
6397
6398   SVN_ERR(svn_sqlite__reset(stmt));
6399
6400   return SVN_NO_ERROR;
6401 }
6402 #endif
6403
6404
6405 struct set_changelist_baton_t
6406 {
6407   const char *new_changelist;
6408   const apr_array_header_t *changelist_filter;
6409   svn_depth_t depth;
6410 };
6411
6412
6413 /* The main part of svn_wc__db_op_set_changelist().
6414  *
6415  * Implements svn_wc__db_txn_callback_t. */
6416 static svn_error_t *
6417 set_changelist_txn(void *baton,
6418                    svn_wc__db_wcroot_t *wcroot,
6419                    const char *local_relpath,
6420                    apr_pool_t *scratch_pool)
6421 {
6422   struct set_changelist_baton_t *scb = baton;
6423   svn_sqlite__stmt_t *stmt;
6424
6425   SVN_ERR(populate_targets_tree(wcroot, local_relpath, scb->depth,
6426                                 scb->changelist_filter, scratch_pool));
6427
6428   /* Ensure we have actual nodes for our targets. */
6429   if (scb->new_changelist)
6430     {
6431       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6432                                         STMT_INSERT_ACTUAL_EMPTIES_FILES));
6433       SVN_ERR(svn_sqlite__step_done(stmt));
6434     }
6435
6436   /* Now create our notification table. */
6437   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6438                                       STMT_CREATE_CHANGELIST_LIST));
6439   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6440                                       STMT_CREATE_CHANGELIST_TRIGGER));
6441
6442   /* Update our changelists. */
6443   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6444                                     STMT_UPDATE_ACTUAL_CHANGELISTS));
6445   SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6446                             scb->new_changelist));
6447   SVN_ERR(svn_sqlite__step_done(stmt));
6448
6449   if (scb->new_changelist)
6450     {
6451       /* We have to notify that we skipped directories, so do that now. */
6452       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6453                                         STMT_MARK_SKIPPED_CHANGELIST_DIRS));
6454       SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
6455                                 scb->new_changelist));
6456       SVN_ERR(svn_sqlite__step_done(stmt));
6457     }
6458
6459   /* We may have left empty ACTUAL nodes, so remove them.  This is only a
6460      potential problem if we removed changelists. */
6461   if (!scb->new_changelist)
6462     {
6463       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6464                                         STMT_DELETE_ACTUAL_EMPTIES));
6465       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6466       SVN_ERR(svn_sqlite__step_done(stmt));
6467     }
6468
6469   return SVN_NO_ERROR;
6470 }
6471
6472
6473 /* Send notifications for svn_wc__db_op_set_changelist().
6474  *
6475  * Implements work_callback_t. */
6476 static svn_error_t *
6477 do_changelist_notify(void *baton,
6478                      svn_wc__db_wcroot_t *wcroot,
6479                      svn_cancel_func_t cancel_func,
6480                      void *cancel_baton,
6481                      svn_wc_notify_func2_t notify_func,
6482                      void *notify_baton,
6483                      apr_pool_t *scratch_pool)
6484 {
6485   svn_sqlite__stmt_t *stmt;
6486   svn_boolean_t have_row;
6487   apr_pool_t *iterpool;
6488
6489   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6490                                     STMT_SELECT_CHANGELIST_LIST));
6491   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6492
6493   iterpool = svn_pool_create(scratch_pool);
6494   while (have_row)
6495     {
6496       /* ### wc_id is column 0. use it one day...  */
6497       const char *notify_relpath = svn_sqlite__column_text(stmt, 1, NULL);
6498       svn_wc_notify_action_t action = svn_sqlite__column_int(stmt, 2);
6499       svn_wc_notify_t *notify;
6500       const char *notify_abspath;
6501
6502       svn_pool_clear(iterpool);
6503
6504       if (cancel_func)
6505         {
6506           svn_error_t *err = cancel_func(cancel_baton);
6507
6508           if (err)
6509             return svn_error_trace(svn_error_compose_create(
6510                                                     err,
6511                                                     svn_sqlite__reset(stmt)));
6512         }
6513
6514       notify_abspath = svn_dirent_join(wcroot->abspath, notify_relpath,
6515                                        iterpool);
6516       notify = svn_wc_create_notify(notify_abspath, action, iterpool);
6517       notify->changelist_name = svn_sqlite__column_text(stmt, 3, NULL);
6518       notify_func(notify_baton, notify, iterpool);
6519
6520       SVN_ERR(svn_sqlite__step(&have_row, stmt));
6521     }
6522   svn_pool_destroy(iterpool);
6523
6524   return svn_error_trace(svn_sqlite__reset(stmt));
6525 }
6526
6527
6528 svn_error_t *
6529 svn_wc__db_op_set_changelist(svn_wc__db_t *db,
6530                              const char *local_abspath,
6531                              const char *new_changelist,
6532                              const apr_array_header_t *changelist_filter,
6533                              svn_depth_t depth,
6534                              svn_wc_notify_func2_t notify_func,
6535                              void *notify_baton,
6536                              svn_cancel_func_t cancel_func,
6537                              void *cancel_baton,
6538                              apr_pool_t *scratch_pool)
6539 {
6540   svn_wc__db_wcroot_t *wcroot;
6541   const char *local_relpath;
6542   struct set_changelist_baton_t scb;
6543
6544   scb.new_changelist = new_changelist;
6545   scb.changelist_filter = changelist_filter;
6546   scb.depth = depth;
6547
6548   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6549
6550   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
6551                                                 db, local_abspath,
6552                                                 scratch_pool, scratch_pool));
6553   VERIFY_USABLE_WCROOT(wcroot);
6554
6555   /* Flush the entries before we do the work. Even if no work is performed,
6556      the flush isn't a problem. */
6557   SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
6558
6559   /* Perform the set-changelist operation (transactionally), perform any
6560      notifications necessary, and then clean out our temporary tables.  */
6561   return svn_error_trace(with_finalization(wcroot, local_relpath,
6562                                            set_changelist_txn, &scb,
6563                                            do_changelist_notify, NULL,
6564                                            cancel_func, cancel_baton,
6565                                            notify_func, notify_baton,
6566                                            STMT_FINALIZE_CHANGELIST,
6567                                            scratch_pool));
6568 }
6569
6570 /* Implementation of svn_wc__db_op_mark_conflict() */
6571 svn_error_t *
6572 svn_wc__db_mark_conflict_internal(svn_wc__db_wcroot_t *wcroot,
6573                                   const char *local_relpath,
6574                                   const svn_skel_t *conflict_skel,
6575                                   apr_pool_t *scratch_pool)
6576 {
6577   svn_sqlite__stmt_t *stmt;
6578   svn_boolean_t got_row;
6579   svn_boolean_t is_complete;
6580
6581   SVN_ERR(svn_wc__conflict_skel_is_complete(&is_complete, conflict_skel));
6582   SVN_ERR_ASSERT(is_complete);
6583
6584   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6585                                     STMT_SELECT_ACTUAL_NODE));
6586   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6587   SVN_ERR(svn_sqlite__step(&got_row, stmt));
6588   SVN_ERR(svn_sqlite__reset(stmt));
6589
6590   if (got_row)
6591     {
6592       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6593                                         STMT_UPDATE_ACTUAL_CONFLICT));
6594       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6595     }
6596   else
6597     {
6598       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6599                                         STMT_INSERT_ACTUAL_CONFLICT));
6600       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6601       if (*local_relpath != '\0')
6602         SVN_ERR(svn_sqlite__bind_text(stmt, 4,
6603                                       svn_relpath_dirname(local_relpath,
6604                                                           scratch_pool)));
6605     }
6606
6607   {
6608     svn_stringbuf_t *sb = svn_skel__unparse(conflict_skel, scratch_pool);
6609
6610     SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6611   }
6612
6613   SVN_ERR(svn_sqlite__update(NULL, stmt));
6614
6615   return SVN_NO_ERROR;
6616 }
6617
6618 svn_error_t *
6619 svn_wc__db_op_mark_conflict(svn_wc__db_t *db,
6620                             const char *local_abspath,
6621                             const svn_skel_t *conflict_skel,
6622                             const svn_skel_t *work_items,
6623                             apr_pool_t *scratch_pool)
6624 {
6625   svn_wc__db_wcroot_t *wcroot;
6626   const char *local_relpath;
6627
6628   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6629
6630   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6631                               local_abspath, scratch_pool, scratch_pool));
6632   VERIFY_USABLE_WCROOT(wcroot);
6633
6634   SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
6635                                             conflict_skel, scratch_pool));
6636
6637   /* ### Should be handled in the same transaction as setting the conflict */
6638   if (work_items)
6639     SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6640
6641   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6642
6643   return SVN_NO_ERROR;
6644
6645 }
6646
6647 /* The body of svn_wc__db_op_mark_resolved().
6648  */
6649 svn_error_t *
6650 svn_wc__db_op_mark_resolved_internal(svn_wc__db_wcroot_t *wcroot,
6651                                      const char *local_relpath,
6652                                      svn_wc__db_t *db,
6653                                      svn_boolean_t resolved_text,
6654                                      svn_boolean_t resolved_props,
6655                                      svn_boolean_t resolved_tree,
6656                                      const svn_skel_t *work_items,
6657                                      apr_pool_t *scratch_pool)
6658 {
6659   svn_sqlite__stmt_t *stmt;
6660   svn_boolean_t have_row;
6661   int total_affected_rows = 0;
6662   svn_boolean_t resolved_all;
6663   apr_size_t conflict_len;
6664   const void *conflict_data;
6665   svn_skel_t *conflicts;
6666
6667   /* Check if we have a conflict in ACTUAL */
6668   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6669                                     STMT_SELECT_ACTUAL_NODE));
6670   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6671
6672   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6673
6674   if (! have_row)
6675     {
6676       SVN_ERR(svn_sqlite__reset(stmt));
6677
6678       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6679                                         STMT_SELECT_NODE_INFO));
6680
6681       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6682
6683       SVN_ERR(svn_sqlite__step(&have_row, stmt));
6684       SVN_ERR(svn_sqlite__reset(stmt));
6685
6686       if (have_row)
6687         return SVN_NO_ERROR;
6688
6689       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6690                                _("The node '%s' was not found."),
6691                                    path_for_error_message(wcroot,
6692                                                           local_relpath,
6693                                                           scratch_pool));
6694     }
6695
6696   conflict_data = svn_sqlite__column_blob(stmt, 2, &conflict_len,
6697                                           scratch_pool);
6698   SVN_ERR(svn_sqlite__reset(stmt));
6699   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
6700
6701   if (!conflict_data)
6702     return SVN_NO_ERROR;
6703
6704   conflicts = svn_skel__parse(conflict_data, conflict_len, scratch_pool);
6705   
6706
6707   SVN_ERR(svn_wc__conflict_skel_resolve(&resolved_all, conflicts,
6708                                         db, wcroot->abspath,
6709                                         resolved_text,
6710                                         resolved_props ? "" : NULL,
6711                                         resolved_tree,
6712                                         scratch_pool, scratch_pool));
6713
6714   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6715                                     STMT_UPDATE_ACTUAL_CONFLICT));
6716   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6717
6718   if (! resolved_all)
6719     {
6720       svn_stringbuf_t *sb = svn_skel__unparse(conflicts, scratch_pool);
6721
6722       SVN_ERR(svn_sqlite__bind_blob(stmt, 3, sb->data, sb->len));
6723     }
6724
6725   SVN_ERR(svn_sqlite__update(&total_affected_rows, stmt));
6726
6727   /* Now, remove the actual node if it doesn't have any more useful
6728      information.  We only need to do this if we've remove data ourselves. */
6729   if (total_affected_rows > 0)
6730     {
6731       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6732                                         STMT_DELETE_ACTUAL_EMPTY));
6733       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6734       SVN_ERR(svn_sqlite__step_done(stmt));
6735     }
6736
6737   return SVN_NO_ERROR;
6738 }
6739
6740 svn_error_t *
6741 svn_wc__db_op_mark_resolved(svn_wc__db_t *db,
6742                             const char *local_abspath,
6743                             svn_boolean_t resolved_text,
6744                             svn_boolean_t resolved_props,
6745                             svn_boolean_t resolved_tree,
6746                             const svn_skel_t *work_items,
6747                             apr_pool_t *scratch_pool)
6748 {
6749   svn_wc__db_wcroot_t *wcroot;
6750   const char *local_relpath;
6751
6752   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
6753
6754   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
6755                               local_abspath, scratch_pool, scratch_pool));
6756   VERIFY_USABLE_WCROOT(wcroot);
6757
6758   SVN_WC__DB_WITH_TXN(
6759     svn_wc__db_op_mark_resolved_internal(
6760                         wcroot, local_relpath, db,
6761                         resolved_text, resolved_props, resolved_tree,
6762                         work_items, scratch_pool),
6763     wcroot);
6764
6765   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
6766   return SVN_NO_ERROR;
6767 }
6768
6769 /* Clear moved-to information at the delete-half of the move which moved
6770  * MOVED_TO_RELPATH here. This transforms the delete part of the move into a
6771  * normal delete.
6772  *
6773  * Note that the moved-to location is always an op-root, while this is not the
6774  * case for a moved-from location.
6775  */
6776 static svn_error_t *
6777 clear_moved_to(svn_wc__db_wcroot_t *wcroot,
6778                const char *moved_to_relpath,
6779                apr_pool_t *scratch_pool)
6780 {
6781   svn_sqlite__stmt_t *stmt;
6782   const char *moved_from_relpath;
6783   int moved_from_op_depth;
6784
6785   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6786                                     STMT_SELECT_MOVED_FROM_RELPATH));
6787   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, moved_to_relpath));
6788   SVN_ERR(svn_sqlite__step_row(stmt));
6789
6790   moved_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
6791   moved_from_op_depth = svn_sqlite__column_int(stmt, 1);
6792   SVN_ERR(svn_sqlite__reset(stmt));
6793
6794   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6795                                     STMT_CLEAR_MOVED_TO_RELPATH));
6796   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6797                             moved_from_relpath, moved_from_op_depth));
6798   SVN_ERR(svn_sqlite__update(NULL, stmt));
6799
6800   return SVN_NO_ERROR;
6801 }
6802
6803 /* Helper function for op_revert_txn. Raises move tree conflicts on
6804    descendants to ensure database stability on a non recursive revert
6805    of an ancestor that contains a possible move related tree conflict.
6806  */
6807 static svn_error_t *
6808 revert_maybe_raise_moved_away(svn_wc__db_wcroot_t * wcroot,
6809                               svn_wc__db_t *db,
6810                               const char *local_relpath,
6811                               int op_depth_below,
6812                               apr_pool_t *scratch_pool)
6813 {
6814   svn_skel_t *conflict;
6815   svn_wc_operation_t operation;
6816   svn_boolean_t tree_conflicted;
6817   const apr_array_header_t *locations;
6818   svn_wc_conflict_reason_t reason;
6819   svn_wc_conflict_action_t action;
6820
6821   SVN_ERR(svn_wc__db_read_conflict_internal(&conflict, NULL, NULL, wcroot,
6822                                             local_relpath,
6823                                             scratch_pool, scratch_pool));
6824
6825   if (!conflict)
6826     return SVN_NO_ERROR;
6827
6828   SVN_ERR(svn_wc__conflict_read_info(&operation, &locations, NULL, NULL,
6829                                      &tree_conflicted,
6830                                      db, wcroot->abspath,
6831                                      conflict,
6832                                      scratch_pool, scratch_pool));
6833
6834   if (!tree_conflicted
6835       || (operation != svn_wc_operation_update
6836           && operation != svn_wc_operation_switch))
6837     {
6838       return SVN_NO_ERROR;
6839     }
6840
6841   SVN_ERR(svn_wc__conflict_read_tree_conflict(&reason, &action,
6842                                               NULL,
6843                                               db, wcroot->abspath,
6844                                               conflict,
6845                                               scratch_pool,
6846                                               scratch_pool));
6847
6848   if (reason == svn_wc_conflict_reason_deleted
6849       || reason == svn_wc_conflict_reason_replaced)
6850     {
6851       SVN_ERR(svn_wc__db_op_raise_moved_away_internal(
6852         wcroot, local_relpath, op_depth_below, db,
6853         operation, action,
6854         (locations && locations->nelts > 0)
6855         ? APR_ARRAY_IDX(locations, 0,
6856                         const svn_wc_conflict_version_t *)
6857         : NULL,
6858         (locations && locations->nelts > 1)
6859         ? APR_ARRAY_IDX(locations, 1,
6860                         const svn_wc_conflict_version_t *)
6861         : NULL,
6862         scratch_pool));
6863
6864       /* Transform the move information into revert information */
6865       SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
6866                                           STMT_MOVE_NOTIFY_TO_REVERT));
6867     }
6868
6869   return SVN_NO_ERROR;
6870 }
6871
6872 /* Baton for op_revert_txn and op_revert_recursive_txn */
6873 struct revert_baton_t
6874 {
6875   svn_wc__db_t *db;
6876   svn_boolean_t clear_changelists;
6877 };
6878
6879 /* One of the two alternative bodies of svn_wc__db_op_revert().
6880  *
6881  * Implements svn_wc__db_txn_callback_t. */
6882 static svn_error_t *
6883 op_revert_txn(void *baton,
6884               svn_wc__db_wcroot_t *wcroot,
6885               const char *local_relpath,
6886               apr_pool_t *scratch_pool)
6887 {
6888   struct revert_baton_t *rvb = baton;
6889   svn_wc__db_t *db = rvb->db;
6890   svn_sqlite__stmt_t *stmt;
6891   svn_boolean_t have_row;
6892   int op_depth;
6893   svn_boolean_t moved_here;
6894   int affected_rows;
6895   const char *moved_to;
6896   int op_depth_below;
6897
6898   /* ### Similar structure to op_revert_recursive_txn, should they be
6899          combined? */
6900
6901   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6902                                     STMT_SELECT_NODE_INFO));
6903   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6904   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6905   if (!have_row)
6906     {
6907       SVN_ERR(svn_sqlite__reset(stmt));
6908
6909       /* There was no NODE row, so attempt to delete an ACTUAL_NODE row.  */
6910       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6911                                         STMT_DELETE_ACTUAL_NODE));
6912       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6913       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
6914       if (affected_rows)
6915         {
6916           /* Can't do non-recursive actual-only revert if actual-only
6917              children exist. Raise an error to cancel the transaction.  */
6918           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6919                                             STMT_ACTUAL_HAS_CHILDREN));
6920           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6921           SVN_ERR(svn_sqlite__step(&have_row, stmt));
6922           SVN_ERR(svn_sqlite__reset(stmt));
6923           if (have_row)
6924             return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6925                                      _("Can't revert '%s' without"
6926                                        " reverting children"),
6927                                      path_for_error_message(wcroot,
6928                                                             local_relpath,
6929                                                             scratch_pool));
6930           return SVN_NO_ERROR;
6931         }
6932
6933       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
6934                                _("The node '%s' was not found."),
6935                                path_for_error_message(wcroot,
6936                                                       local_relpath,
6937                                                       scratch_pool));
6938     }
6939
6940   op_depth = svn_sqlite__column_int(stmt, 0);
6941   moved_here = svn_sqlite__column_boolean(stmt, 15);
6942   moved_to = svn_sqlite__column_text(stmt, 17, scratch_pool);
6943
6944   SVN_ERR(svn_sqlite__step(&have_row, stmt));
6945   if (have_row)
6946     op_depth_below = svn_sqlite__column_int(stmt, 0);
6947   else
6948     op_depth_below = -1;
6949
6950   SVN_ERR(svn_sqlite__reset(stmt));
6951
6952   if (moved_to)
6953     {
6954       SVN_ERR(svn_wc__db_op_break_move_internal(wcroot,
6955                                                 local_relpath, op_depth,
6956                                                 moved_to, NULL, scratch_pool));
6957     }
6958
6959   if (op_depth > 0 && op_depth == relpath_depth(local_relpath))
6960     {
6961       int op_depth_increased;
6962
6963       /* Can't do non-recursive revert if children exist */
6964       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6965                                         STMT_SELECT_GE_OP_DEPTH_CHILDREN));
6966       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6967                                 local_relpath, op_depth));
6968       SVN_ERR(svn_sqlite__step(&have_row, stmt));
6969       SVN_ERR(svn_sqlite__reset(stmt));
6970       if (have_row)
6971         return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
6972                                  _("Can't revert '%s' without"
6973                                    " reverting children"),
6974                                  path_for_error_message(wcroot,
6975                                                         local_relpath,
6976                                                         scratch_pool));
6977
6978       /* Rewrite the op-depth of all deleted children making the
6979          direct children into roots of deletes. */
6980       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6981                                      STMT_UPDATE_OP_DEPTH_INCREASE_RECURSIVE));
6982       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
6983                                 local_relpath,
6984                                 op_depth));
6985       SVN_ERR(svn_sqlite__update(&op_depth_increased, stmt));
6986
6987       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6988                                         STMT_DELETE_WORKING_NODE));
6989       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6990       SVN_ERR(svn_sqlite__step_done(stmt));
6991
6992       /* ### This removes the lock, but what about the access baton? */
6993       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
6994                                         STMT_DELETE_WC_LOCK_ORPHAN));
6995       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
6996       SVN_ERR(svn_sqlite__step_done(stmt));
6997
6998       /* If this node was moved-here, clear moved-to at the move source. */
6999       if (moved_here)
7000         SVN_ERR(clear_moved_to(wcroot, local_relpath, scratch_pool));
7001
7002       /* If the node was moved itself, we don't have interesting moved
7003          children (and the move itself was already broken) */
7004       if (op_depth_increased && !moved_to)
7005         SVN_ERR(revert_maybe_raise_moved_away(wcroot, db, local_relpath,
7006                                               op_depth_below, scratch_pool));
7007     }
7008
7009   if (rvb->clear_changelists)
7010     {
7011       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7012                                         STMT_DELETE_ACTUAL_NODE));
7013       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7014       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7015     }
7016   else
7017     {
7018       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7019                                   STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST));
7020       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7021       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7022       if (!affected_rows)
7023         {
7024           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7025                                   STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST));
7026           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7027           SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7028         }
7029     }
7030
7031   return SVN_NO_ERROR;
7032 }
7033
7034
7035 /* One of the two alternative bodies of svn_wc__db_op_revert().
7036  *
7037  * Implements svn_wc__db_txn_callback_t. */
7038 static svn_error_t *
7039 op_revert_recursive_txn(void *baton,
7040                         svn_wc__db_wcroot_t *wcroot,
7041                         const char *local_relpath,
7042                         apr_pool_t *scratch_pool)
7043 {
7044   struct revert_baton_t *rvb = baton;
7045   svn_sqlite__stmt_t *stmt;
7046   svn_boolean_t have_row;
7047   int op_depth;
7048   int select_op_depth;
7049   svn_boolean_t moved_here;
7050   int affected_rows;
7051   apr_pool_t *iterpool;
7052
7053   /* ### Similar structure to op_revert_txn, should they be
7054          combined? */
7055
7056   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7057                                     STMT_SELECT_NODE_INFO));
7058   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7059   SVN_ERR(svn_sqlite__step(&have_row, stmt));
7060   if (!have_row)
7061     {
7062       SVN_ERR(svn_sqlite__reset(stmt));
7063
7064       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7065                                         STMT_DELETE_ACTUAL_NODE));
7066       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7067                                 local_relpath));
7068       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7069
7070       if (affected_rows)
7071         return SVN_NO_ERROR;  /* actual-only revert */
7072
7073       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7074                                _("The node '%s' was not found."),
7075                                path_for_error_message(wcroot,
7076                                                       local_relpath,
7077                                                       scratch_pool));
7078     }
7079
7080   op_depth = svn_sqlite__column_int(stmt, 0);
7081   moved_here = svn_sqlite__column_boolean(stmt, 15);
7082   SVN_ERR(svn_sqlite__reset(stmt));
7083
7084   if (op_depth > 0 && op_depth != relpath_depth(local_relpath))
7085     return svn_error_createf(SVN_ERR_WC_INVALID_OPERATION_DEPTH, NULL,
7086                              _("Can't revert '%s' without"
7087                                " reverting parent"),
7088                              path_for_error_message(wcroot,
7089                                                     local_relpath,
7090                                                     scratch_pool));
7091
7092   /* Remove moved-here from move destinations outside the tree. */
7093   SVN_ERR(svn_sqlite__get_statement(
7094                     &stmt, wcroot->sdb, STMT_SELECT_MOVED_OUTSIDE));
7095   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
7096                             op_depth));
7097   SVN_ERR(svn_sqlite__step(&have_row, stmt));
7098   while (have_row)
7099     {
7100       const char *src_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7101       const char *dst_relpath = svn_sqlite__column_text(stmt, 1, NULL);
7102       int move_op_depth = svn_sqlite__column_int(stmt, 2);
7103       svn_error_t *err;
7104
7105       err = svn_wc__db_op_break_move_internal(wcroot,
7106                                               src_relpath, move_op_depth,
7107                                               dst_relpath, NULL, scratch_pool);
7108       if (err)
7109         return svn_error_compose_create(err, svn_sqlite__reset(stmt));
7110
7111       SVN_ERR(svn_sqlite__step(&have_row, stmt));
7112     }
7113   SVN_ERR(svn_sqlite__reset(stmt));
7114
7115   /* Don't delete BASE nodes */
7116   select_op_depth = op_depth ? op_depth : 1;
7117
7118   /* Reverting any non wc-root node */
7119   SVN_ERR(svn_sqlite__get_statement(
7120                     &stmt, wcroot->sdb,
7121                     STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
7122   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
7123                             local_relpath, select_op_depth));
7124   SVN_ERR(svn_sqlite__step_done(stmt));
7125
7126   if (rvb->clear_changelists)
7127     {
7128       SVN_ERR(svn_sqlite__get_statement(
7129                         &stmt, wcroot->sdb,
7130                         STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7131       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7132       SVN_ERR(svn_sqlite__step_done(stmt));
7133     }
7134   else
7135     {
7136       SVN_ERR(svn_sqlite__get_statement(
7137                         &stmt, wcroot->sdb,
7138                         STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
7139       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7140       SVN_ERR(svn_sqlite__step_done(stmt));
7141
7142       SVN_ERR(svn_sqlite__get_statement(
7143                         &stmt, wcroot->sdb,
7144                         STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
7145       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7146       SVN_ERR(svn_sqlite__step_done(stmt));
7147     }
7148
7149   /* ### This removes the locks, but what about the access batons? */
7150   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7151                                     STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
7152   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7153                             local_relpath));
7154   SVN_ERR(svn_sqlite__step_done(stmt));
7155
7156   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7157                                     STMT_SELECT_MOVED_HERE_CHILDREN));
7158   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7159
7160   SVN_ERR(svn_sqlite__step(&have_row, stmt));
7161
7162   iterpool = svn_pool_create(scratch_pool);
7163   while (have_row)
7164     {
7165       const char *moved_here_child_relpath;
7166       svn_error_t *err;
7167
7168       svn_pool_clear(iterpool);
7169
7170       moved_here_child_relpath = svn_sqlite__column_text(stmt, 0, iterpool);
7171       err = clear_moved_to(wcroot, moved_here_child_relpath, iterpool);
7172       if (err)
7173         return svn_error_trace(svn_error_compose_create(
7174                                         err,
7175                                         svn_sqlite__reset(stmt)));
7176
7177       SVN_ERR(svn_sqlite__step(&have_row, stmt));
7178     }
7179   SVN_ERR(svn_sqlite__reset(stmt));
7180   svn_pool_destroy(iterpool);
7181
7182   /* Clear potential moved-to pointing at the target node itself. */
7183   if (op_depth > 0 && op_depth == relpath_depth(local_relpath)
7184       && moved_here)
7185     SVN_ERR(clear_moved_to(wcroot, local_relpath, scratch_pool));
7186
7187   return SVN_NO_ERROR;
7188 }
7189
7190 svn_error_t *
7191 svn_wc__db_op_revert(svn_wc__db_t *db,
7192                      const char *local_abspath,
7193                      svn_depth_t depth,
7194                      svn_boolean_t clear_changelists,
7195                      apr_pool_t *result_pool,
7196                      apr_pool_t *scratch_pool)
7197 {
7198   svn_wc__db_wcroot_t *wcroot;
7199   const char *local_relpath;
7200   struct revert_baton_t rvb;
7201   struct with_triggers_baton_t wtb = { STMT_CREATE_REVERT_LIST,
7202                                        STMT_DROP_REVERT_LIST_TRIGGERS,
7203                                        NULL, NULL};
7204
7205   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7206
7207   rvb.db = db;
7208   rvb.clear_changelists = clear_changelists;
7209   wtb.cb_baton = &rvb;
7210
7211   switch (depth)
7212     {
7213     case svn_depth_empty:
7214       wtb.cb_func = op_revert_txn;
7215       break;
7216     case svn_depth_infinity:
7217       wtb.cb_func = op_revert_recursive_txn;
7218       break;
7219     default:
7220       return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
7221                                _("Unsupported depth for revert of '%s'"),
7222                                svn_dirent_local_style(local_abspath,
7223                                                       scratch_pool));
7224     }
7225
7226   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7227                               db, local_abspath, scratch_pool, scratch_pool));
7228   VERIFY_USABLE_WCROOT(wcroot);
7229
7230   SVN_WC__DB_WITH_TXN(with_triggers(&wtb, wcroot, local_relpath, scratch_pool),
7231                       wcroot);
7232
7233   SVN_ERR(flush_entries(wcroot, local_abspath, depth, scratch_pool));
7234
7235   return SVN_NO_ERROR;
7236 }
7237
7238 /* The body of svn_wc__db_revert_list_read().
7239  */
7240 static svn_error_t *
7241 revert_list_read(svn_boolean_t *reverted,
7242                  const apr_array_header_t **marker_paths,
7243                  svn_boolean_t *copied_here,
7244                  svn_node_kind_t *kind,
7245                  svn_wc__db_wcroot_t *wcroot,
7246                  const char *local_relpath,
7247                  svn_wc__db_t *db,
7248                  apr_pool_t *result_pool,
7249                  apr_pool_t *scratch_pool)
7250 {
7251   svn_sqlite__stmt_t *stmt;
7252   svn_boolean_t have_row;
7253
7254   *reverted = FALSE;
7255   *marker_paths = NULL;
7256   *copied_here = FALSE;
7257   *kind = svn_node_unknown;
7258
7259   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7260                                     STMT_SELECT_REVERT_LIST));
7261   SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7262   SVN_ERR(svn_sqlite__step(&have_row, stmt));
7263   if (have_row)
7264     {
7265       svn_boolean_t is_actual = svn_sqlite__column_boolean(stmt, 0);
7266       svn_boolean_t another_row = FALSE;
7267
7268       if (is_actual)
7269         {
7270           apr_size_t conflict_len;
7271           const void *conflict_data;
7272
7273           conflict_data = svn_sqlite__column_blob(stmt, 5, &conflict_len,
7274                                                   scratch_pool);
7275           if (conflict_data)
7276             {
7277               svn_skel_t *conflicts = svn_skel__parse(conflict_data,
7278                                                       conflict_len,
7279                                                       scratch_pool);
7280
7281               SVN_ERR(svn_wc__conflict_read_markers(marker_paths,
7282                                                     db, wcroot->abspath,
7283                                                     conflicts,
7284                                                     result_pool,
7285                                                     scratch_pool));
7286             }
7287
7288           if (!svn_sqlite__column_is_null(stmt, 1)) /* notify */
7289             *reverted = TRUE;
7290
7291           SVN_ERR(svn_sqlite__step(&another_row, stmt));
7292         }
7293
7294       if (!is_actual || another_row)
7295         {
7296           *reverted = TRUE;
7297           if (!svn_sqlite__column_is_null(stmt, 4)) /* repos_id */
7298             {
7299               int op_depth = svn_sqlite__column_int(stmt, 3);
7300               *copied_here = (op_depth == relpath_depth(local_relpath));
7301             }
7302           *kind = svn_sqlite__column_token(stmt, 2, kind_map);
7303         }
7304
7305     }
7306   SVN_ERR(svn_sqlite__reset(stmt));
7307
7308   if (have_row)
7309     {
7310       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7311                                         STMT_DELETE_REVERT_LIST));
7312       SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7313       SVN_ERR(svn_sqlite__step_done(stmt));
7314     }
7315
7316   return SVN_NO_ERROR;
7317 }
7318
7319 svn_error_t *
7320 svn_wc__db_revert_list_read(svn_boolean_t *reverted,
7321                             const apr_array_header_t **marker_files,
7322                             svn_boolean_t *copied_here,
7323                             svn_node_kind_t *kind,
7324                             svn_wc__db_t *db,
7325                             const char *local_abspath,
7326                             apr_pool_t *result_pool,
7327                             apr_pool_t *scratch_pool)
7328 {
7329   svn_wc__db_wcroot_t *wcroot;
7330   const char *local_relpath;
7331
7332   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7333                               db, local_abspath, scratch_pool, scratch_pool));
7334   VERIFY_USABLE_WCROOT(wcroot);
7335
7336   SVN_WC__DB_WITH_TXN(
7337     revert_list_read(reverted, marker_files, copied_here, kind,
7338                      wcroot, local_relpath, db,
7339                      result_pool, scratch_pool),
7340     wcroot);
7341   return SVN_NO_ERROR;
7342 }
7343
7344
7345 /* The body of svn_wc__db_revert_list_read_copied_children().
7346  */
7347 static svn_error_t *
7348 revert_list_read_copied_children(svn_wc__db_wcroot_t *wcroot,
7349                                  const char *local_relpath,
7350                                  apr_array_header_t **children_p,
7351                                  apr_pool_t *result_pool,
7352                                  apr_pool_t *scratch_pool)
7353 {
7354   svn_sqlite__stmt_t *stmt;
7355   svn_boolean_t have_row;
7356   apr_array_header_t *children;
7357
7358   children =
7359     apr_array_make(result_pool, 0,
7360                   sizeof(svn_wc__db_revert_list_copied_child_info_t *));
7361
7362   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7363                                     STMT_SELECT_REVERT_LIST_COPIED_CHILDREN));
7364   SVN_ERR(svn_sqlite__bindf(stmt, "sd",
7365                             local_relpath, relpath_depth(local_relpath)));
7366   SVN_ERR(svn_sqlite__step(&have_row, stmt));
7367   while (have_row)
7368     {
7369       svn_wc__db_revert_list_copied_child_info_t *child_info;
7370       const char *child_relpath;
7371
7372       child_info = apr_palloc(result_pool, sizeof(*child_info));
7373
7374       child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7375       child_info->abspath = svn_dirent_join(wcroot->abspath, child_relpath,
7376                                             result_pool);
7377       child_info->kind = svn_sqlite__column_token(stmt, 1, kind_map);
7378       APR_ARRAY_PUSH(
7379         children,
7380         svn_wc__db_revert_list_copied_child_info_t *) = child_info;
7381
7382       SVN_ERR(svn_sqlite__step(&have_row, stmt));
7383     }
7384    SVN_ERR(svn_sqlite__reset(stmt));
7385
7386   *children_p = children;
7387
7388   return SVN_NO_ERROR;
7389 }
7390
7391
7392 svn_error_t *
7393 svn_wc__db_revert_list_read_copied_children(apr_array_header_t **children,
7394                                             svn_wc__db_t *db,
7395                                             const char *local_abspath,
7396                                             apr_pool_t *result_pool,
7397                                             apr_pool_t *scratch_pool)
7398 {
7399   svn_wc__db_wcroot_t *wcroot;
7400   const char *local_relpath;
7401
7402   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7403                               db, local_abspath, scratch_pool, scratch_pool));
7404   VERIFY_USABLE_WCROOT(wcroot);
7405
7406   SVN_WC__DB_WITH_TXN(
7407     revert_list_read_copied_children(wcroot, local_relpath, children,
7408                                      result_pool, scratch_pool),
7409     wcroot);
7410   return SVN_NO_ERROR;
7411 }
7412
7413
7414 svn_error_t *
7415 svn_wc__db_revert_list_notify(svn_wc_notify_func2_t notify_func,
7416                               void *notify_baton,
7417                               svn_wc__db_t *db,
7418                               const char *local_abspath,
7419                               apr_pool_t *scratch_pool)
7420 {
7421   svn_wc__db_wcroot_t *wcroot;
7422   const char *local_relpath;
7423   svn_sqlite__stmt_t *stmt;
7424   svn_boolean_t have_row;
7425   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
7426
7427   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7428                               db, local_abspath, scratch_pool, iterpool));
7429   VERIFY_USABLE_WCROOT(wcroot);
7430
7431   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7432                                     STMT_SELECT_REVERT_LIST_RECURSIVE));
7433   SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7434   SVN_ERR(svn_sqlite__step(&have_row, stmt));
7435   if (!have_row)
7436     return svn_error_trace(svn_sqlite__reset(stmt)); /* optimise for no row */
7437   while (have_row)
7438     {
7439       const char *notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7440       svn_wc_notify_t *notify;
7441
7442       svn_pool_clear(iterpool);
7443
7444       notify = svn_wc_create_notify(svn_dirent_join(wcroot->abspath,
7445                                                     notify_relpath,
7446                                                     iterpool),
7447                                     svn_wc_notify_revert,
7448                                     iterpool);
7449
7450       if (!svn_sqlite__column_is_null(stmt, 1))
7451         notify->kind = svn_sqlite__column_token(stmt, 1, kind_map);
7452       else
7453         {
7454           if (!svn_sqlite__column_is_null(stmt, 3))
7455             notify->kind = svn_sqlite__column_token(stmt, 3, kind_map_none);
7456
7457           switch (svn_sqlite__column_int(stmt, 2))
7458             {
7459               case 0:
7460                 continue;
7461               case 1:
7462                 /* standard revert */
7463                 break;
7464               case 2:
7465                 notify->action = svn_wc_notify_tree_conflict;
7466                 break;
7467               default:
7468                 SVN_ERR_MALFUNCTION();
7469             }
7470         }
7471
7472       notify_func(notify_baton, notify, iterpool);
7473
7474       SVN_ERR(svn_sqlite__step(&have_row, stmt));
7475     }
7476   SVN_ERR(svn_sqlite__reset(stmt));
7477
7478   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7479                                     STMT_DELETE_REVERT_LIST_RECURSIVE));
7480   SVN_ERR(svn_sqlite__bindf(stmt, "s", local_relpath));
7481   SVN_ERR(svn_sqlite__step_done(stmt));
7482
7483   svn_pool_destroy(iterpool);
7484
7485   return SVN_NO_ERROR;
7486 }
7487
7488 svn_error_t *
7489 svn_wc__db_revert_list_done(svn_wc__db_t *db,
7490                             const char *local_abspath,
7491                             apr_pool_t *scratch_pool)
7492 {
7493   svn_wc__db_wcroot_t *wcroot;
7494   const char *local_relpath;
7495
7496   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
7497                               db, local_abspath, scratch_pool, scratch_pool));
7498   VERIFY_USABLE_WCROOT(wcroot);
7499
7500   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_DROP_REVERT_LIST));
7501
7502   return SVN_NO_ERROR;
7503 }
7504
7505 /* The body of svn_wc__db_op_remove_node().
7506  */
7507 static svn_error_t *
7508 remove_node_txn(svn_boolean_t *left_changes,
7509                 svn_wc__db_wcroot_t *wcroot,
7510                 const char *local_relpath,
7511                 svn_wc__db_t *db,
7512                 svn_boolean_t destroy_wc,
7513                 svn_boolean_t destroy_changes,
7514                 const svn_skel_t *conflict,
7515                 const svn_skel_t *work_items,
7516                 svn_cancel_func_t cancel_func,
7517                 void *cancel_baton,
7518                 apr_pool_t *scratch_pool)
7519 {
7520   svn_sqlite__stmt_t *stmt;
7521
7522   /* Note that unlike many similar functions it is a valid scenario for this
7523      function to be called on a wcroot! */
7524
7525    /* db set when destroying wc */
7526   SVN_ERR_ASSERT(!destroy_wc || db != NULL);
7527
7528   if (left_changes)
7529     *left_changes = FALSE;
7530
7531   if (destroy_wc
7532       && (!destroy_changes || *local_relpath == '\0'))
7533     {
7534       svn_boolean_t have_row;
7535       apr_pool_t *iterpool;
7536       svn_error_t *err = NULL;
7537
7538       /* Install WQ items for deleting the unmodified files and all dirs */
7539       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7540                                         STMT_SELECT_WORKING_PRESENT));
7541       SVN_ERR(svn_sqlite__bindf(stmt, "is",
7542                                 wcroot->wc_id, local_relpath));
7543
7544       SVN_ERR(svn_sqlite__step(&have_row, stmt));
7545
7546       iterpool = svn_pool_create(scratch_pool);
7547
7548       while (have_row)
7549         {
7550           const char *child_relpath;
7551           const char *child_abspath;
7552           svn_node_kind_t child_kind;
7553           svn_boolean_t have_checksum;
7554           svn_filesize_t recorded_size;
7555           apr_int64_t recorded_time;
7556           const svn_io_dirent2_t *dirent;
7557           svn_boolean_t modified_p = TRUE;
7558           svn_skel_t *work_item = NULL;
7559
7560           svn_pool_clear(iterpool);
7561
7562           child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
7563           child_kind = svn_sqlite__column_token(stmt, 1, kind_map);
7564
7565           child_abspath = svn_dirent_join(wcroot->abspath, child_relpath,
7566                                           iterpool);
7567
7568           if (child_kind == svn_node_file)
7569             {
7570               have_checksum = !svn_sqlite__column_is_null(stmt, 2);
7571               recorded_size = get_recorded_size(stmt, 3);
7572               recorded_time = svn_sqlite__column_int64(stmt, 4);
7573             }
7574
7575           if (cancel_func)
7576             err = cancel_func(cancel_baton);
7577
7578           if (err)
7579             break;
7580
7581           err = svn_io_stat_dirent2(&dirent, child_abspath, FALSE, TRUE,
7582                                     iterpool, iterpool);
7583
7584           if (err)
7585             break;
7586
7587           if (destroy_changes
7588               || dirent->kind != svn_node_file
7589               || child_kind != svn_node_file)
7590             {
7591               /* Not interested in keeping changes */
7592               modified_p = FALSE;
7593             }
7594           else if (child_kind == svn_node_file
7595                    && dirent->kind == svn_node_file
7596                    && dirent->filesize == recorded_size
7597                    && dirent->mtime == recorded_time)
7598             {
7599               modified_p = FALSE; /* File matches recorded state */
7600             }
7601           else if (have_checksum)
7602             err = svn_wc__internal_file_modified_p(&modified_p,
7603                                                    db, child_abspath,
7604                                                    FALSE, iterpool);
7605
7606           if (err)
7607             break;
7608
7609           if (modified_p)
7610             {
7611               if (left_changes)
7612                 *left_changes = TRUE;
7613             }
7614           else if (child_kind == svn_node_dir)
7615             {
7616               err = svn_wc__wq_build_dir_remove(&work_item,
7617                                                 db, wcroot->abspath,
7618                                                 child_abspath, FALSE,
7619                                                 iterpool, iterpool);
7620             }
7621           else /* svn_node_file || svn_node_symlink */
7622             {
7623               err = svn_wc__wq_build_file_remove(&work_item,
7624                                                  db, wcroot->abspath,
7625                                                  child_abspath,
7626                                                  iterpool, iterpool);
7627             }
7628
7629           if (err)
7630             break;
7631
7632           if (work_item)
7633             {
7634               err = add_work_items(wcroot->sdb, work_item, iterpool);
7635               if (err)
7636                 break;
7637             }
7638
7639           SVN_ERR(svn_sqlite__step(&have_row, stmt));
7640         }
7641       svn_pool_destroy(iterpool);
7642
7643       SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
7644     }
7645
7646   if (destroy_wc && *local_relpath != '\0')
7647     {
7648       /* Create work item for destroying the root */
7649       svn_wc__db_status_t status;
7650       svn_node_kind_t kind;
7651       SVN_ERR(read_info(&status, &kind, NULL, NULL, NULL, NULL, NULL, NULL,
7652                         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7653                         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
7654                         wcroot, local_relpath,
7655                         scratch_pool, scratch_pool));
7656
7657       if (status == svn_wc__db_status_normal
7658           || status == svn_wc__db_status_added
7659           || status == svn_wc__db_status_incomplete)
7660         {
7661           svn_skel_t *work_item = NULL;
7662           const char *local_abspath = svn_dirent_join(wcroot->abspath,
7663                                                           local_relpath,
7664                                                           scratch_pool);
7665
7666           if (kind == svn_node_dir)
7667             {
7668               SVN_ERR(svn_wc__wq_build_dir_remove(&work_item,
7669                                                   db, wcroot->abspath,
7670                                                   local_abspath,
7671                                                   destroy_changes
7672                                                       /* recursive */,
7673                                                   scratch_pool, scratch_pool));
7674             }
7675           else
7676             {
7677               svn_boolean_t modified_p = FALSE;
7678
7679               if (!destroy_changes)
7680                 {
7681                   SVN_ERR(svn_wc__internal_file_modified_p(&modified_p,
7682                                                            db, local_abspath,
7683                                                            FALSE,
7684                                                            scratch_pool));
7685                 }
7686
7687               if (!modified_p)
7688                 SVN_ERR(svn_wc__wq_build_file_remove(&work_item,
7689                                                      db, wcroot->abspath,
7690                                                      local_abspath,
7691                                                      scratch_pool,
7692                                                      scratch_pool));
7693               else
7694                 {
7695                   if (left_changes)
7696                     *left_changes = TRUE;
7697                 }
7698             }
7699
7700           SVN_ERR(add_work_items(wcroot->sdb, work_item, scratch_pool));
7701         }
7702     }
7703
7704   /* Remove all nodes below local_relpath */
7705   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7706                                     STMT_DELETE_NODE_RECURSIVE));
7707   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7708   SVN_ERR(svn_sqlite__step_done(stmt));
7709
7710   /* Delete the root NODE when this is not the working copy root */
7711   if (local_relpath[0] != '\0')
7712     {
7713       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7714                                         STMT_DELETE_NODE_ALL_LAYERS));
7715       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7716       SVN_ERR(svn_sqlite__step_done(stmt));
7717     }
7718
7719   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7720                                     STMT_DELETE_ACTUAL_NODE_RECURSIVE));
7721
7722   /* Delete all actual nodes at or below local_relpath */
7723   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
7724                                          local_relpath));
7725   SVN_ERR(svn_sqlite__step_done(stmt));
7726
7727   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
7728   if (conflict)
7729     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
7730                                               conflict, scratch_pool));
7731
7732   return SVN_NO_ERROR;
7733 }
7734
7735 svn_error_t *
7736 svn_wc__db_op_remove_node(svn_boolean_t *left_changes,
7737                           svn_wc__db_t *db,
7738                           const char *local_abspath,
7739                           svn_boolean_t destroy_wc,
7740                           svn_boolean_t destroy_changes,
7741                           const svn_skel_t *conflict,
7742                           const svn_skel_t *work_items,
7743                           svn_cancel_func_t cancel_func,
7744                           void *cancel_baton,
7745                           apr_pool_t *scratch_pool)
7746 {
7747   svn_wc__db_wcroot_t *wcroot;
7748   const char *local_relpath;
7749
7750   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7751
7752   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7753                               local_abspath, scratch_pool, scratch_pool));
7754   VERIFY_USABLE_WCROOT(wcroot);
7755
7756   SVN_WC__DB_WITH_TXN(remove_node_txn(left_changes,
7757                                       wcroot, local_relpath, db,
7758                                       destroy_wc, destroy_changes,
7759                                       conflict, work_items,
7760                                       cancel_func, cancel_baton, scratch_pool),
7761                       wcroot);
7762
7763   /* Flush everything below this node in all ways */
7764   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
7765                         scratch_pool));
7766
7767   return SVN_NO_ERROR;
7768 }
7769
7770
7771 /* The body of svn_wc__db_op_set_base_depth().
7772  */
7773 static svn_error_t *
7774 db_op_set_base_depth(svn_wc__db_wcroot_t *wcroot,
7775                      const char *local_relpath,
7776                      svn_depth_t depth,
7777                      apr_pool_t *scratch_pool)
7778 {
7779   svn_sqlite__stmt_t *stmt;
7780   int affected_rows;
7781
7782   /* Flush any entries before we start monkeying the database.  */
7783   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7784                                     STMT_UPDATE_NODE_BASE_DEPTH));
7785   SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath,
7786                             svn_token__to_word(depth_map, depth)));
7787   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
7788
7789   if (affected_rows == 0)
7790     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
7791                              _("The node '%s' is not a committed directory"),
7792                              path_for_error_message(wcroot, local_relpath,
7793                                                     scratch_pool));
7794
7795   return SVN_NO_ERROR;
7796 }
7797
7798
7799 svn_error_t *
7800 svn_wc__db_op_set_base_depth(svn_wc__db_t *db,
7801                              const char *local_abspath,
7802                              svn_depth_t depth,
7803                              apr_pool_t *scratch_pool)
7804 {
7805   svn_wc__db_wcroot_t *wcroot;
7806   const char *local_relpath;
7807
7808   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
7809   SVN_ERR_ASSERT(depth >= svn_depth_empty && depth <= svn_depth_infinity);
7810
7811   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
7812                               local_abspath, scratch_pool, scratch_pool));
7813   VERIFY_USABLE_WCROOT(wcroot);
7814
7815   /* ### We set depth on working and base to match entry behavior.
7816          Maybe these should be separated later? */
7817   SVN_WC__DB_WITH_TXN(db_op_set_base_depth(wcroot, local_relpath, depth,
7818                                            scratch_pool),
7819                       wcroot);
7820
7821   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
7822
7823   return SVN_NO_ERROR;
7824 }
7825
7826
7827 static svn_error_t *
7828 info_below_working(svn_boolean_t *have_base,
7829                    svn_boolean_t *have_work,
7830                    svn_wc__db_status_t *status,
7831                    svn_wc__db_wcroot_t *wcroot,
7832                    const char *local_relpath,
7833                    int below_op_depth, /* < 0 is ignored */
7834                    apr_pool_t *scratch_pool);
7835
7836
7837 /* Convert STATUS, the raw status obtained from the presence map, to
7838    the status appropriate for a working (op_depth > 0) node and return
7839    it in *WORKING_STATUS. */
7840 static svn_error_t *
7841 convert_to_working_status(svn_wc__db_status_t *working_status,
7842                           svn_wc__db_status_t status)
7843 {
7844   svn_wc__db_status_t work_status = status;
7845
7846   SVN_ERR_ASSERT(work_status == svn_wc__db_status_normal
7847                  || work_status == svn_wc__db_status_not_present
7848                  || work_status == svn_wc__db_status_base_deleted
7849                  || work_status == svn_wc__db_status_incomplete
7850                  || work_status == svn_wc__db_status_excluded);
7851
7852   if (work_status == svn_wc__db_status_excluded)
7853     {
7854       *working_status = svn_wc__db_status_excluded;
7855     }
7856   else if (work_status == svn_wc__db_status_not_present
7857            || work_status == svn_wc__db_status_base_deleted)
7858     {
7859       /* The caller should scan upwards to detect whether this
7860          deletion has occurred because this node has been moved
7861          away, or it is a regular deletion. Also note that the
7862          deletion could be of the BASE tree, or a child of
7863          something that has been copied/moved here. */
7864
7865       *working_status = svn_wc__db_status_deleted;
7866     }
7867   else /* normal or incomplete */
7868     {
7869       /* The caller should scan upwards to detect whether this
7870          addition has occurred because of a simple addition,
7871          a copy, or is the destination of a move. */
7872       *working_status = svn_wc__db_status_added;
7873     }
7874
7875   return SVN_NO_ERROR;
7876 }
7877
7878
7879 /* Return the status of the node, if any, below the "working" node (or
7880    below BELOW_OP_DEPTH if >= 0).
7881    Set *HAVE_BASE or *HAVE_WORK to indicate if a base node or lower
7882    working node is present, and *STATUS to the status of the first
7883    layer below the selected node. */
7884 static svn_error_t *
7885 info_below_working(svn_boolean_t *have_base,
7886                    svn_boolean_t *have_work,
7887                    svn_wc__db_status_t *status,
7888                    svn_wc__db_wcroot_t *wcroot,
7889                    const char *local_relpath,
7890                    int below_op_depth,
7891                    apr_pool_t *scratch_pool)
7892 {
7893   svn_sqlite__stmt_t *stmt;
7894   svn_boolean_t have_row;
7895
7896   *have_base = *have_work =  FALSE;
7897   *status = svn_wc__db_status_normal;
7898
7899   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7900                                     STMT_SELECT_NODE_INFO));
7901   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
7902   SVN_ERR(svn_sqlite__step(&have_row, stmt));
7903
7904   if (below_op_depth >= 0)
7905     {
7906       while (have_row &&
7907              (svn_sqlite__column_int(stmt, 0) > below_op_depth))
7908         {
7909           SVN_ERR(svn_sqlite__step(&have_row, stmt));
7910         }
7911     }
7912   if (have_row)
7913     {
7914       SVN_ERR(svn_sqlite__step(&have_row, stmt));
7915       if (have_row)
7916         *status = svn_sqlite__column_token(stmt, 3, presence_map);
7917
7918       while (have_row)
7919         {
7920           int op_depth = svn_sqlite__column_int(stmt, 0);
7921
7922           if (op_depth > 0)
7923             *have_work = TRUE;
7924           else
7925             *have_base = TRUE;
7926
7927           SVN_ERR(svn_sqlite__step(&have_row, stmt));
7928         }
7929     }
7930   SVN_ERR(svn_sqlite__reset(stmt));
7931
7932   if (*have_work)
7933     SVN_ERR(convert_to_working_status(status, *status));
7934
7935   return SVN_NO_ERROR;
7936 }
7937
7938 /* Helper function for op_delete_txn */
7939 static svn_error_t *
7940 delete_update_movedto(svn_wc__db_wcroot_t *wcroot,
7941                       const char *child_moved_from_relpath,
7942                       int op_depth,
7943                       const char *new_moved_to_relpath,
7944                       apr_pool_t *scratch_pool)
7945 {
7946   svn_sqlite__stmt_t *stmt;
7947   int affected;
7948
7949   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
7950                                     STMT_UPDATE_MOVED_TO_RELPATH));
7951
7952   SVN_ERR(svn_sqlite__bindf(stmt, "isds",
7953                             wcroot->wc_id,
7954                             child_moved_from_relpath,
7955                             op_depth,
7956                             new_moved_to_relpath));
7957   SVN_ERR(svn_sqlite__update(&affected, stmt));
7958 #ifdef SVN_DEBUG
7959   /* Not fatal in release mode. The move recording is broken,
7960      but the rest of the working copy can handle this. */
7961   SVN_ERR_ASSERT(affected == 1);
7962 #endif
7963
7964   return SVN_NO_ERROR;
7965 }
7966
7967
7968 struct op_delete_baton_t {
7969   const char *moved_to_relpath; /* NULL if delete is not part of a move */
7970   svn_skel_t *conflict;
7971   svn_skel_t *work_items;
7972   svn_boolean_t delete_dir_externals;
7973   svn_boolean_t notify;
7974 };
7975
7976 /* This structure is used while rewriting move information for nodes.
7977  *
7978  * The most simple case of rewriting move information happens when
7979  * a moved-away subtree is moved again:  mv A B; mv B C
7980  * The second move requires rewriting moved-to info at or within A.
7981  *
7982  * Another example is a move of a subtree which had nodes moved into it:
7983  *   mv A B/F; mv B G
7984  * This requires rewriting such that A/F is marked has having moved to G/F.
7985  *
7986  * Another case is where a node becomes a nested moved node.
7987  * A nested move happens when a subtree child is moved before or after
7988  * the subtree itself is moved. For example:
7989  *   mv A/F A/G; mv A B
7990  * In this case, the move A/F -> A/G is rewritten to B/F -> B/G.
7991  * Note that the following sequence results in the same DB state:
7992  *   mv A B; mv B/F B/G
7993  * We do not care about the order the moves were performed in.
7994  * For details, see http://wiki.apache.org/subversion/MultiLayerMoves
7995  */
7996 struct moved_node_t {
7997   /* The source of the move. */
7998   const char *local_relpath;
7999
8000   /* The move destination. */
8001   const char *moved_to_relpath;
8002
8003   /* The op-depth of the deleted node at the source of the move. */
8004   int op_depth;
8005
8006   /* When >= 1 the op_depth at which local_relpath was moved to its
8007      location. Used to find its original location outside the delete */
8008   int moved_from_depth;
8009 };
8010
8011 /* Helper function to resolve the original location of local_relpath at OP_DEPTH
8012    before it was moved into the tree rooted at ROOT_RELPATH. */
8013 static svn_error_t *
8014 resolve_moved_from(const char **moved_from_relpath,
8015                    int *moved_from_op_depth,
8016                    svn_wc__db_wcroot_t *wcroot,
8017                    const char *root_relpath,
8018                    const char *local_relpath,
8019                    int op_depth,
8020                    apr_pool_t *result_pool,
8021                    apr_pool_t *scratch_pool)
8022 {
8023   const char *suffix = "";
8024   svn_sqlite__stmt_t *stmt;
8025   const char *m_from_relpath;
8026   int m_from_op_depth;
8027   int m_move_from_depth;
8028   svn_boolean_t have_row;
8029
8030   while (relpath_depth(local_relpath) > op_depth)
8031     {
8032       const char *name;
8033       svn_relpath_split(&local_relpath, &name, local_relpath, scratch_pool);
8034       suffix = svn_relpath_join(suffix, name, scratch_pool);
8035     }
8036
8037   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8038                                     STMT_SELECT_MOVED_FROM_FOR_DELETE));
8039   SVN_ERR(svn_sqlite__bindf(stmt, "is",
8040                             wcroot->wc_id, local_relpath));
8041   SVN_ERR(svn_sqlite__step(&have_row, stmt));
8042
8043   if (!have_row)
8044     {
8045       /* assert(have_row); */
8046       *moved_from_relpath = NULL;
8047       *moved_from_op_depth = -1;
8048
8049       SVN_ERR(svn_sqlite__reset(stmt));
8050
8051       return SVN_NO_ERROR;
8052     }
8053
8054   m_from_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
8055   m_from_op_depth = svn_sqlite__column_int(stmt, 1);
8056   m_move_from_depth = svn_sqlite__column_int(stmt, 2);
8057
8058   SVN_ERR(svn_sqlite__reset(stmt));
8059
8060   if (! svn_relpath_skip_ancestor(root_relpath, m_from_relpath))
8061     {
8062       *moved_from_relpath = svn_relpath_join(m_from_relpath, suffix,
8063                                              result_pool);
8064       *moved_from_op_depth = m_from_op_depth; /* ### Ok? */
8065       return SVN_NO_ERROR;
8066     }
8067   else if (!m_move_from_depth)
8068     {
8069       *moved_from_relpath = NULL;
8070       *moved_from_op_depth = -1;
8071       return SVN_NO_ERROR;
8072     }
8073
8074   return svn_error_trace(
8075         resolve_moved_from(moved_from_relpath,
8076                            moved_from_op_depth,
8077                            wcroot,
8078                            root_relpath,
8079                            svn_relpath_join(m_from_relpath, suffix,
8080                                             scratch_pool),
8081                            m_move_from_depth,
8082                            result_pool, scratch_pool));
8083 }
8084
8085 static svn_error_t *
8086 delete_node(void *baton,
8087             svn_wc__db_wcroot_t *wcroot,
8088             const char *local_relpath,
8089             apr_pool_t *scratch_pool)
8090 {
8091   struct op_delete_baton_t *b = baton;
8092   svn_wc__db_status_t status;
8093   svn_boolean_t have_row, op_root;
8094   svn_boolean_t add_work = FALSE;
8095   svn_sqlite__stmt_t *stmt;
8096   int working_op_depth; /* Depth of what is to be deleted */
8097   int keep_op_depth = 0; /* Depth of what is below what is deleted */
8098   svn_node_kind_t kind;
8099   apr_array_header_t *moved_nodes = NULL;
8100   int delete_op_depth = relpath_depth(local_relpath);
8101
8102   assert(*local_relpath); /* Can't delete wcroot */
8103
8104   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8105                                     STMT_SELECT_NODE_INFO));
8106   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8107   SVN_ERR(svn_sqlite__step(&have_row, stmt));
8108
8109   if (!have_row)
8110     {
8111       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
8112                                svn_sqlite__reset(stmt),
8113                                _("The node '%s' was not found."),
8114                                path_for_error_message(wcroot,
8115                                                       local_relpath,
8116                                                       scratch_pool));
8117     }
8118
8119   working_op_depth = svn_sqlite__column_int(stmt, 0);
8120   status = svn_sqlite__column_token(stmt, 3, presence_map);
8121   kind = svn_sqlite__column_token(stmt, 4, kind_map);
8122
8123   if (working_op_depth < delete_op_depth)
8124     {
8125       op_root = FALSE;
8126       add_work = TRUE;
8127       keep_op_depth = working_op_depth;
8128     }
8129   else
8130     {
8131       op_root = TRUE;
8132
8133       SVN_ERR(svn_sqlite__step(&have_row, stmt));
8134
8135       if (have_row)
8136         {
8137           svn_wc__db_status_t below_status;
8138           int below_op_depth;
8139
8140           below_op_depth = svn_sqlite__column_int(stmt, 0);
8141           below_status = svn_sqlite__column_token(stmt, 3, presence_map);
8142
8143           if (below_status != svn_wc__db_status_not_present
8144               && below_status != svn_wc__db_status_base_deleted)
8145             {
8146               add_work = TRUE;
8147               keep_op_depth = below_op_depth;
8148             }
8149           else
8150             keep_op_depth = 0;
8151         }
8152       else
8153         keep_op_depth = -1;
8154     }
8155
8156   SVN_ERR(svn_sqlite__reset(stmt));
8157
8158   if (working_op_depth != 0) /* WORKING */
8159     SVN_ERR(convert_to_working_status(&status, status));
8160
8161   if (status == svn_wc__db_status_deleted
8162       || status == svn_wc__db_status_not_present)
8163     return SVN_NO_ERROR;
8164
8165   /* Don't copy BASE directories with server excluded nodes */
8166   if (status == svn_wc__db_status_normal && kind == svn_node_dir)
8167     {
8168       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8169                                         STMT_HAS_SERVER_EXCLUDED_DESCENDANTS));
8170       SVN_ERR(svn_sqlite__bindf(stmt, "is",
8171                                 wcroot->wc_id, local_relpath));
8172       SVN_ERR(svn_sqlite__step(&have_row, stmt));
8173       if (have_row)
8174         {
8175           const char *absent_path = svn_sqlite__column_text(stmt, 0,
8176                                                             scratch_pool);
8177
8178           return svn_error_createf(
8179                                SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
8180                                svn_sqlite__reset(stmt),
8181                           _("Cannot delete '%s' as '%s' is excluded by server"),
8182                                path_for_error_message(wcroot, local_relpath,
8183                                                       scratch_pool),
8184                                path_for_error_message(wcroot, absent_path,
8185                                                       scratch_pool));
8186         }
8187       SVN_ERR(svn_sqlite__reset(stmt));
8188     }
8189   else if (status == svn_wc__db_status_server_excluded)
8190     {
8191       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
8192                           _("Cannot delete '%s' as it is excluded by server"),
8193                                path_for_error_message(wcroot, local_relpath,
8194                                                       scratch_pool));
8195     }
8196   else if (status == svn_wc__db_status_excluded)
8197     {
8198       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
8199                           _("Cannot delete '%s' as it is excluded"),
8200                                path_for_error_message(wcroot, local_relpath,
8201                                                       scratch_pool));
8202     }
8203
8204   if (b->moved_to_relpath)
8205     {
8206       const char *moved_from_relpath = NULL;
8207       struct moved_node_t *moved_node;
8208       int move_op_depth;
8209
8210       moved_nodes = apr_array_make(scratch_pool, 1,
8211                                    sizeof(struct moved_node_t *));
8212
8213       /* The node is being moved-away.
8214        * Figure out if the node was moved-here before, or whether this
8215        * is the first time the node is moved. */
8216       if (status == svn_wc__db_status_added)
8217         SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL,
8218                               &moved_from_relpath,
8219                               NULL,
8220                               &move_op_depth,
8221                               wcroot, local_relpath,
8222                               scratch_pool, scratch_pool));
8223
8224       if (op_root && moved_from_relpath)
8225         {
8226           const char *part = svn_relpath_skip_ancestor(local_relpath,
8227                                                        moved_from_relpath);
8228
8229           /* Existing move-root is moved to another location */
8230           moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
8231           if (!part)
8232             moved_node->local_relpath = moved_from_relpath;
8233           else
8234             moved_node->local_relpath = svn_relpath_join(b->moved_to_relpath,
8235                                                          part, scratch_pool);
8236           moved_node->op_depth = move_op_depth;
8237           moved_node->moved_to_relpath = b->moved_to_relpath;
8238           moved_node->moved_from_depth = -1;
8239
8240           APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
8241         }
8242       else if (!op_root && (status == svn_wc__db_status_normal
8243                             || status == svn_wc__db_status_copied
8244                             || status == svn_wc__db_status_moved_here))
8245         {
8246           /* The node is becoming a move-root for the first time,
8247            * possibly because of a nested move operation. */
8248           moved_node = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
8249           moved_node->local_relpath = local_relpath;
8250           moved_node->op_depth = delete_op_depth;
8251           moved_node->moved_to_relpath = b->moved_to_relpath;
8252           moved_node->moved_from_depth = -1;
8253
8254           APR_ARRAY_PUSH(moved_nodes, const struct moved_node_t *) = moved_node;
8255         }
8256       /* Else: We can't track history of local additions and/or of things we are
8257                about to delete. */
8258
8259       /* And update all moved_to values still pointing to this location */
8260       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8261                                         STMT_UPDATE_MOVED_TO_DESCENDANTS));
8262       SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id,
8263                                              local_relpath,
8264                                              b->moved_to_relpath));
8265       SVN_ERR(svn_sqlite__update(NULL, stmt));
8266     }
8267
8268   /* Find children that were moved out of the subtree rooted at this node.
8269    * We'll need to update their op-depth columns because their deletion
8270    * is now implied by the deletion of their parent (i.e. this node). */
8271     {
8272       apr_pool_t *iterpool;
8273       int i;
8274
8275       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8276                                         STMT_SELECT_MOVED_FOR_DELETE));
8277       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
8278                                 delete_op_depth));
8279
8280       SVN_ERR(svn_sqlite__step(&have_row, stmt));
8281       iterpool = svn_pool_create(scratch_pool);
8282       while (have_row)
8283         {
8284           struct moved_node_t *mn;
8285           const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8286           const char *mv_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
8287           int child_op_depth = svn_sqlite__column_int(stmt, 2);
8288           int moved_from_depth = -1;
8289           svn_boolean_t fixup = FALSE;
8290
8291           if (! b->moved_to_relpath
8292               && ! svn_relpath_skip_ancestor(local_relpath, mv_to_relpath))
8293             {
8294               /* a NULL moved_here_depth will be reported as 0 */
8295               int moved_here_depth = svn_sqlite__column_int(stmt, 3);
8296
8297               /* Plain delete. Fixup move information of descendants that were
8298                  moved here, or that were moved out */
8299
8300               if (moved_here_depth >= delete_op_depth)
8301                 {
8302                   /* The move we recorded here must be moved to the location
8303                      this node had before it was moved here.
8304
8305                      This might contain multiple steps when the node was moved
8306                      in several places within the to be deleted tree */
8307
8308                   /* ### TODO: Add logic */
8309                   fixup = TRUE;
8310                   moved_from_depth = moved_here_depth;
8311                 }
8312               else
8313                 {
8314                   /* Update the op-depth of an moved away node that was
8315                      registered as moved by the records that we are about
8316                      to delete */
8317                   fixup = TRUE;
8318                   child_op_depth = delete_op_depth;
8319                 }
8320             }
8321           else if (b->moved_to_relpath)
8322             {
8323               /* The node is moved to a new location */
8324
8325               if (delete_op_depth == child_op_depth)
8326                 {
8327                   /* Update the op-depth of a tree shadowed by this tree */
8328                   fixup = TRUE;
8329                   /*child_op_depth = delete_depth;*/
8330                 }
8331               else if (child_op_depth >= delete_op_depth
8332                        && !svn_relpath_skip_ancestor(local_relpath,
8333                                                      mv_to_relpath))
8334                 {
8335                   /* Update the move destination of something that is now moved
8336                      away further */
8337
8338                   child_relpath = svn_relpath_skip_ancestor(local_relpath,
8339                                                             child_relpath);
8340
8341                   if (child_relpath)
8342                     {
8343                       child_relpath = svn_relpath_join(b->moved_to_relpath,
8344                                                        child_relpath,
8345                                                        scratch_pool);
8346
8347                       if (child_op_depth > delete_op_depth
8348                            && svn_relpath_skip_ancestor(local_relpath,
8349                                                         child_relpath))
8350                         child_op_depth = delete_op_depth;
8351                       else
8352                         {
8353                           /* Calculate depth of the shadowing at the new location */
8354                           child_op_depth = child_op_depth
8355                                                 - relpath_depth(local_relpath)
8356                                                 + relpath_depth(b->moved_to_relpath);
8357                         }
8358
8359                       fixup = TRUE;
8360                     }
8361                 }
8362             }
8363
8364           if (fixup)
8365             {
8366               mn = apr_palloc(scratch_pool, sizeof(struct moved_node_t));
8367
8368               mn->local_relpath = apr_pstrdup(scratch_pool, child_relpath);
8369               mn->moved_to_relpath = apr_pstrdup(scratch_pool, mv_to_relpath);
8370               mn->op_depth = child_op_depth;
8371               mn->moved_from_depth = moved_from_depth;
8372
8373               if (!moved_nodes)
8374                 moved_nodes = apr_array_make(scratch_pool, 1,
8375                                              sizeof(struct moved_node_t *));
8376               APR_ARRAY_PUSH(moved_nodes, struct moved_node_t *) = mn;
8377             }
8378
8379           SVN_ERR(svn_sqlite__step(&have_row, stmt));
8380         }
8381       SVN_ERR(svn_sqlite__reset(stmt));
8382
8383       for (i = 0; moved_nodes && (i < moved_nodes->nelts); i++)
8384         {
8385           struct moved_node_t *mn = APR_ARRAY_IDX(moved_nodes, i,
8386                                                   struct moved_node_t *);
8387
8388           if (mn->moved_from_depth > 0)
8389             {
8390               svn_pool_clear(iterpool);
8391
8392               SVN_ERR(resolve_moved_from(&mn->local_relpath, &mn->op_depth,
8393                                          wcroot, local_relpath,
8394                                          mn->local_relpath,
8395                                          mn->moved_from_depth,
8396                                          scratch_pool, iterpool));
8397
8398               if (!mn->local_relpath)
8399                 svn_sort__array_delete(moved_nodes, i--, 1);
8400             }
8401         }
8402
8403       svn_pool_destroy(iterpool);
8404     }
8405
8406   if (!b->moved_to_relpath)
8407     {
8408       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8409                                         STMT_CLEAR_MOVED_TO_DESCENDANTS));
8410       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8411                                 local_relpath));
8412       SVN_ERR(svn_sqlite__update(NULL, stmt));
8413
8414       if (op_root)
8415         {
8416           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8417                                             STMT_CLEAR_MOVED_TO_FROM_DEST));
8418           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8419                                     local_relpath));
8420
8421           SVN_ERR(svn_sqlite__update(NULL, stmt));
8422         }
8423     }
8424
8425
8426   /* ### Put actual-only nodes into the list? */
8427   if (b->notify)
8428     {
8429       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8430                                         STMT_INSERT_DELETE_LIST));
8431       SVN_ERR(svn_sqlite__bindf(stmt, "isd",
8432                                 wcroot->wc_id, local_relpath, working_op_depth));
8433       SVN_ERR(svn_sqlite__step_done(stmt));
8434     }
8435
8436   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8437                                     STMT_DELETE_NODES_ABOVE_DEPTH_RECURSIVE));
8438   SVN_ERR(svn_sqlite__bindf(stmt, "isd",
8439                             wcroot->wc_id, local_relpath, delete_op_depth));
8440   SVN_ERR(svn_sqlite__step_done(stmt));
8441
8442   /* Delete ACTUAL_NODE rows, but leave those that have changelist
8443      and a NODES row. */
8444   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8445                          STMT_DELETE_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8446   SVN_ERR(svn_sqlite__bindf(stmt, "is",
8447                             wcroot->wc_id, local_relpath));
8448   SVN_ERR(svn_sqlite__step_done(stmt));
8449
8450   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8451                          STMT_CLEAR_ACTUAL_NODE_LEAVING_CHANGELIST_RECURSIVE));
8452   SVN_ERR(svn_sqlite__bindf(stmt, "is",
8453                             wcroot->wc_id, local_relpath));
8454   SVN_ERR(svn_sqlite__step_done(stmt));
8455
8456   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8457                                     STMT_DELETE_WC_LOCK_ORPHAN_RECURSIVE));
8458   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
8459                             local_relpath));
8460   SVN_ERR(svn_sqlite__step_done(stmt));
8461
8462   if (add_work)
8463     {
8464       /* Delete the node at LOCAL_RELPATH, and possibly mark it as moved. */
8465
8466       /* Delete the node and possible descendants. */
8467       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8468                                  STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
8469       SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
8470                                 wcroot->wc_id, local_relpath,
8471                                 keep_op_depth, delete_op_depth));
8472       SVN_ERR(svn_sqlite__step_done(stmt));
8473     }
8474
8475   if (moved_nodes)
8476     {
8477       int i;
8478
8479       for (i = 0; i < moved_nodes->nelts; ++i)
8480         {
8481           const struct moved_node_t *moved_node
8482             = APR_ARRAY_IDX(moved_nodes, i, void *);
8483
8484           SVN_ERR(delete_update_movedto(wcroot,
8485                                         moved_node->local_relpath,
8486                                         moved_node->op_depth,
8487                                         moved_node->moved_to_relpath,
8488                                         scratch_pool));
8489         }
8490     }
8491
8492   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8493                                     STMT_DELETE_FILE_EXTERNALS));
8494   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8495   SVN_ERR(svn_sqlite__step_done(stmt));
8496
8497   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8498                                     b->delete_dir_externals
8499                                     ? STMT_DELETE_EXTERNAL_REGISTATIONS
8500                                     : STMT_DELETE_FILE_EXTERNAL_REGISTATIONS));
8501   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
8502   SVN_ERR(svn_sqlite__step_done(stmt));
8503
8504   SVN_ERR(add_work_items(wcroot->sdb, b->work_items, scratch_pool));
8505   if (b->conflict)
8506     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
8507                                               b->conflict, scratch_pool));
8508
8509   return SVN_NO_ERROR;
8510 }
8511
8512 static svn_error_t *
8513 op_delete_txn(void *baton,
8514               svn_wc__db_wcroot_t *wcroot,
8515               const char *local_relpath,
8516               apr_pool_t *scratch_pool)
8517 {
8518
8519   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8520   SVN_ERR(delete_node(baton, wcroot, local_relpath, scratch_pool));
8521   return SVN_NO_ERROR;
8522 }
8523
8524
8525 struct op_delete_many_baton_t {
8526   apr_array_header_t *rel_targets;
8527   svn_boolean_t delete_dir_externals;
8528   const svn_skel_t *work_items;
8529 };
8530
8531 static svn_error_t *
8532 op_delete_many_txn(void *baton,
8533                    svn_wc__db_wcroot_t *wcroot,
8534                    const char *local_relpath,
8535                    apr_pool_t *scratch_pool)
8536 {
8537   struct op_delete_many_baton_t *odmb = baton;
8538   struct op_delete_baton_t odb;
8539   int i;
8540   apr_pool_t *iterpool;
8541
8542   odb.moved_to_relpath = NULL;
8543   odb.conflict = NULL;
8544   odb.work_items = NULL;
8545   odb.delete_dir_externals = odmb->delete_dir_externals;
8546   odb.notify = TRUE;
8547
8548   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_CREATE_DELETE_LIST));
8549   iterpool = svn_pool_create(scratch_pool);
8550   for (i = 0; i < odmb->rel_targets->nelts; i++)
8551     {
8552       const char *target_relpath = APR_ARRAY_IDX(odmb->rel_targets, i,
8553                                                  const char *);
8554
8555
8556       svn_pool_clear(iterpool);
8557       SVN_ERR(delete_node(&odb, wcroot, target_relpath, iterpool));
8558     }
8559   svn_pool_destroy(iterpool);
8560
8561   SVN_ERR(add_work_items(wcroot->sdb, odmb->work_items, scratch_pool));
8562
8563   return SVN_NO_ERROR;
8564 }
8565
8566
8567 static svn_error_t *
8568 do_delete_notify(void *baton,
8569                  svn_wc__db_wcroot_t *wcroot,
8570                  svn_cancel_func_t cancel_func,
8571                  void *cancel_baton,
8572                  svn_wc_notify_func2_t notify_func,
8573                  void *notify_baton,
8574                  apr_pool_t *scratch_pool)
8575 {
8576   svn_sqlite__stmt_t *stmt;
8577   svn_boolean_t have_row;
8578   apr_pool_t *iterpool;
8579
8580   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
8581                                     STMT_SELECT_DELETE_LIST));
8582   SVN_ERR(svn_sqlite__step(&have_row, stmt));
8583
8584   iterpool = svn_pool_create(scratch_pool);
8585   while (have_row)
8586     {
8587       const char *notify_relpath;
8588       const char *notify_abspath;
8589
8590       svn_pool_clear(iterpool);
8591
8592       notify_relpath = svn_sqlite__column_text(stmt, 0, NULL);
8593       notify_abspath = svn_dirent_join(wcroot->abspath,
8594                                        notify_relpath,
8595                                        iterpool);
8596
8597       notify_func(notify_baton,
8598                   svn_wc_create_notify(notify_abspath,
8599                                        svn_wc_notify_delete,
8600                                        iterpool),
8601                   iterpool);
8602
8603       SVN_ERR(svn_sqlite__step(&have_row, stmt));
8604     }
8605   svn_pool_destroy(iterpool);
8606
8607   SVN_ERR(svn_sqlite__reset(stmt));
8608
8609   /* We only allow cancellation after notification for all deleted nodes
8610    * has happened. The nodes are already deleted so we should notify for
8611    * all of them. */
8612   if (cancel_func)
8613     SVN_ERR(cancel_func(cancel_baton));
8614
8615   return SVN_NO_ERROR;
8616 }
8617
8618
8619 svn_error_t *
8620 svn_wc__db_op_delete(svn_wc__db_t *db,
8621                      const char *local_abspath,
8622                      const char *moved_to_abspath,
8623                      svn_boolean_t delete_dir_externals,
8624                      svn_skel_t *conflict,
8625                      svn_skel_t *work_items,
8626                      svn_cancel_func_t cancel_func,
8627                      void *cancel_baton,
8628                      svn_wc_notify_func2_t notify_func,
8629                      void *notify_baton,
8630                      apr_pool_t *scratch_pool)
8631 {
8632   svn_wc__db_wcroot_t *wcroot;
8633   svn_wc__db_wcroot_t *moved_to_wcroot;
8634   const char *local_relpath;
8635   const char *moved_to_relpath;
8636   struct op_delete_baton_t odb;
8637
8638   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8639
8640   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8641                                                 db, local_abspath,
8642                                                 scratch_pool, scratch_pool));
8643   VERIFY_USABLE_WCROOT(wcroot);
8644
8645   if (moved_to_abspath)
8646     {
8647       SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&moved_to_wcroot,
8648                                                     &moved_to_relpath,
8649                                                     db, moved_to_abspath,
8650                                                     scratch_pool,
8651                                                     scratch_pool));
8652       VERIFY_USABLE_WCROOT(moved_to_wcroot);
8653
8654       if (strcmp(wcroot->abspath, moved_to_wcroot->abspath) != 0)
8655         return svn_error_createf(SVN_ERR_UNSUPPORTED_FEATURE, NULL,
8656                                  _("Cannot move '%s' to '%s' because they "
8657                                    "are not in the same working copy"),
8658                                  svn_dirent_local_style(local_abspath,
8659                                                         scratch_pool),
8660                                  svn_dirent_local_style(moved_to_abspath,
8661                                                         scratch_pool));
8662     }
8663   else
8664     moved_to_relpath = NULL;
8665
8666   odb.moved_to_relpath = moved_to_relpath;
8667   odb.conflict = conflict;
8668   odb.work_items = work_items;
8669   odb.delete_dir_externals = delete_dir_externals;
8670
8671   if (notify_func)
8672     {
8673       /* Perform the deletion operation (transactionally), perform any
8674          notifications necessary, and then clean out our temporary tables.  */
8675       odb.notify = TRUE;
8676       SVN_ERR(with_finalization(wcroot, local_relpath,
8677                                 op_delete_txn, &odb,
8678                                 do_delete_notify, NULL,
8679                                 cancel_func, cancel_baton,
8680                                 notify_func, notify_baton,
8681                                 STMT_FINALIZE_DELETE,
8682                                 scratch_pool));
8683     }
8684   else
8685     {
8686       /* Avoid the trigger work */
8687       odb.notify = FALSE;
8688       SVN_WC__DB_WITH_TXN(
8689                     delete_node(&odb, wcroot, local_relpath, scratch_pool),
8690                     wcroot);
8691     }
8692
8693   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_infinity,
8694                         scratch_pool));
8695
8696   return SVN_NO_ERROR;
8697 }
8698
8699
8700 svn_error_t *
8701 svn_wc__db_op_delete_many(svn_wc__db_t *db,
8702                           apr_array_header_t *targets,
8703                           svn_boolean_t delete_dir_externals,
8704                           const svn_skel_t *work_items,
8705                           svn_cancel_func_t cancel_func,
8706                           void *cancel_baton,
8707                           svn_wc_notify_func2_t notify_func,
8708                           void *notify_baton,
8709                           apr_pool_t *scratch_pool)
8710 {
8711   svn_wc__db_wcroot_t *wcroot;
8712   const char *local_relpath;
8713   struct op_delete_many_baton_t odmb;
8714   int i;
8715   apr_pool_t *iterpool;
8716
8717   odmb.rel_targets = apr_array_make(scratch_pool, targets->nelts,
8718                                     sizeof(const char *));
8719   odmb.work_items = work_items;
8720   odmb.delete_dir_externals = delete_dir_externals;
8721   iterpool = svn_pool_create(scratch_pool);
8722   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
8723                                                 db,
8724                                                 APR_ARRAY_IDX(targets, 0,
8725                                                               const char *),
8726                                                 scratch_pool, iterpool));
8727   VERIFY_USABLE_WCROOT(wcroot);
8728   for (i = 0; i < targets->nelts; i++)
8729     {
8730       const char *local_abspath = APR_ARRAY_IDX(targets, i, const char*);
8731       svn_wc__db_wcroot_t *target_wcroot;
8732
8733       svn_pool_clear(iterpool);
8734
8735       SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&target_wcroot,
8736                                                     &local_relpath, db,
8737                                                     APR_ARRAY_IDX(targets, i,
8738                                                                   const char *),
8739                                                     scratch_pool, iterpool));
8740       VERIFY_USABLE_WCROOT(target_wcroot);
8741       SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
8742
8743       /* Assert that all targets are within the same working copy. */
8744       SVN_ERR_ASSERT(wcroot->wc_id == target_wcroot->wc_id);
8745
8746       APR_ARRAY_PUSH(odmb.rel_targets, const char *) = local_relpath;
8747       SVN_ERR(flush_entries(target_wcroot, local_abspath, svn_depth_infinity,
8748                             iterpool));
8749
8750     }
8751   svn_pool_destroy(iterpool);
8752
8753   /* Perform the deletion operation (transactionally), perform any
8754      notifications necessary, and then clean out our temporary tables.  */
8755   return svn_error_trace(with_finalization(wcroot, wcroot->abspath,
8756                                            op_delete_many_txn, &odmb,
8757                                            do_delete_notify, NULL,
8758                                            cancel_func, cancel_baton,
8759                                            notify_func, notify_baton,
8760                                            STMT_FINALIZE_DELETE,
8761                                            scratch_pool));
8762 }
8763
8764 /* Helper function for read_info() to provide better diagnostics than just
8765    asserting.
8766
8767    ### BH: Yes this code is ugly, and that is why I only introduce it in
8768    ### read_info(). But we really need something to determine the root cause
8769    ### of this problem to diagnose why TortoiseSVN users were seeing all those
8770    ### assertions.
8771
8772    Adds an error to the *err chain if invalid values are encountered. In that
8773    case the value is set to the first value in the map, assuming that caller
8774    will just return the combined error.
8775  */
8776 static int
8777 column_token_err(svn_error_t **err,
8778                  svn_sqlite__stmt_t *stmt,
8779                  int column,
8780                  const svn_token_map_t *map)
8781 {
8782   svn_error_t *err2;
8783   const char *word = svn_sqlite__column_text(stmt, column, NULL);
8784   int value;
8785
8786   /* svn_token__from_word_err() handles NULL for us */
8787   err2 = svn_token__from_word_err(&value, map, word);
8788
8789   if (err2)
8790     {
8791       *err = svn_error_compose_create(
8792                 *err,
8793                 svn_error_createf(
8794                     SVN_ERR_WC_CORRUPT, err2,
8795                     _("Encountered invalid node state in column %d of "
8796                       "info query to working copy database"),
8797                     column));
8798       value = map[0].val;
8799     }
8800
8801   return value;
8802 }
8803
8804 /* Like svn_wc__db_read_info(), but taking WCROOT+LOCAL_RELPATH instead of
8805    DB+LOCAL_ABSPATH, and outputting repos ids instead of URL+UUID. */
8806 static svn_error_t *
8807 read_info(svn_wc__db_status_t *status,
8808           svn_node_kind_t *kind,
8809           svn_revnum_t *revision,
8810           const char **repos_relpath,
8811           apr_int64_t *repos_id,
8812           svn_revnum_t *changed_rev,
8813           apr_time_t *changed_date,
8814           const char **changed_author,
8815           svn_depth_t *depth,
8816           const svn_checksum_t **checksum,
8817           const char **target,
8818           const char **original_repos_relpath,
8819           apr_int64_t *original_repos_id,
8820           svn_revnum_t *original_revision,
8821           svn_wc__db_lock_t **lock,
8822           svn_filesize_t *recorded_size,
8823           apr_time_t *recorded_time,
8824           const char **changelist,
8825           svn_boolean_t *conflicted,
8826           svn_boolean_t *op_root,
8827           svn_boolean_t *had_props,
8828           svn_boolean_t *props_mod,
8829           svn_boolean_t *have_base,
8830           svn_boolean_t *have_more_work,
8831           svn_boolean_t *have_work,
8832           svn_wc__db_wcroot_t *wcroot,
8833           const char *local_relpath,
8834           apr_pool_t *result_pool,
8835           apr_pool_t *scratch_pool)
8836 {
8837   svn_sqlite__stmt_t *stmt_info;
8838   svn_sqlite__stmt_t *stmt_act;
8839   svn_boolean_t have_info;
8840   svn_boolean_t have_act;
8841   svn_error_t *err = NULL;
8842
8843   /* Obtain the most likely to exist record first, to make sure we don't
8844      have to obtain the SQLite read-lock multiple times */
8845   SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
8846                                     lock ? STMT_SELECT_NODE_INFO_WITH_LOCK
8847                                          : STMT_SELECT_NODE_INFO));
8848   SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
8849   SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
8850
8851   if (changelist || conflicted || props_mod)
8852     {
8853       SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
8854                                         STMT_SELECT_ACTUAL_NODE));
8855       SVN_ERR(svn_sqlite__bindf(stmt_act, "is", wcroot->wc_id, local_relpath));
8856       SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
8857     }
8858   else
8859     {
8860       have_act = FALSE;
8861       stmt_act = NULL;
8862     }
8863
8864   if (have_info)
8865     {
8866       int op_depth;
8867       svn_node_kind_t node_kind;
8868
8869       op_depth = svn_sqlite__column_int(stmt_info, 0);
8870       node_kind = column_token_err(&err, stmt_info, 4, kind_map);
8871
8872       if (status)
8873         {
8874           *status = column_token_err(&err, stmt_info, 3, presence_map);
8875
8876           if (op_depth != 0) /* WORKING */
8877             err = svn_error_compose_create(err,
8878                                            convert_to_working_status(status,
8879                                                                      *status));
8880         }
8881       if (kind)
8882         {
8883           *kind = node_kind;
8884         }
8885       if (op_depth != 0)
8886         {
8887           if (repos_id)
8888             *repos_id = INVALID_REPOS_ID;
8889           if (revision)
8890             *revision = SVN_INVALID_REVNUM;
8891           if (repos_relpath)
8892             /* Our path is implied by our parent somewhere up the tree.
8893                With the NULL value and status, the caller will know to
8894                search up the tree for the base of our path.  */
8895             *repos_relpath = NULL;
8896         }
8897       else
8898         {
8899           /* Fetch repository information. If we have a
8900              WORKING_NODE (and have been added), then the repository
8901              we're being added to will be dependent upon a parent. The
8902              caller can scan upwards to locate the repository.  */
8903           repos_location_from_columns(repos_id, revision, repos_relpath,
8904                                       stmt_info, 1, 5, 2, result_pool);
8905         }
8906       if (changed_rev)
8907         {
8908           *changed_rev = svn_sqlite__column_revnum(stmt_info, 8);
8909         }
8910       if (changed_date)
8911         {
8912           *changed_date = svn_sqlite__column_int64(stmt_info, 9);
8913         }
8914       if (changed_author)
8915         {
8916           *changed_author = svn_sqlite__column_text(stmt_info, 10,
8917                                                     result_pool);
8918         }
8919       if (recorded_time)
8920         {
8921           *recorded_time = svn_sqlite__column_int64(stmt_info, 13);
8922         }
8923       if (depth)
8924         {
8925           if (node_kind != svn_node_dir)
8926             *depth = svn_depth_unknown;
8927           else if (svn_sqlite__column_is_null(stmt_info, 11))
8928             *depth = svn_depth_unknown;
8929           else
8930             *depth = column_token_err(&err, stmt_info, 11, depth_map);
8931         }
8932       if (checksum)
8933         {
8934           if (node_kind != svn_node_file)
8935             {
8936               *checksum = NULL;
8937             }
8938           else
8939             {
8940
8941               err = svn_error_compose_create(
8942                         err, svn_sqlite__column_checksum(checksum, stmt_info, 6,
8943                                                          result_pool));
8944             }
8945         }
8946       if (recorded_size)
8947         {
8948           *recorded_size = get_recorded_size(stmt_info, 7);
8949         }
8950       if (target)
8951         {
8952           if (node_kind != svn_node_symlink)
8953             *target = NULL;
8954           else
8955             *target = svn_sqlite__column_text(stmt_info, 12, result_pool);
8956         }
8957       if (changelist)
8958         {
8959           if (have_act)
8960             *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
8961           else
8962             *changelist = NULL;
8963         }
8964       if (op_depth == 0)
8965         {
8966           if (original_repos_id)
8967             *original_repos_id = INVALID_REPOS_ID;
8968           if (original_revision)
8969             *original_revision = SVN_INVALID_REVNUM;
8970           if (original_repos_relpath)
8971             *original_repos_relpath = NULL;
8972         }
8973       else
8974         {
8975           repos_location_from_columns(original_repos_id,
8976                                       original_revision,
8977                                       original_repos_relpath,
8978                                       stmt_info, 1, 5, 2, result_pool);
8979         }
8980       if (props_mod)
8981         {
8982           *props_mod = have_act && !svn_sqlite__column_is_null(stmt_act, 1);
8983         }
8984       if (had_props)
8985         {
8986           *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt_info, 14);
8987         }
8988       if (conflicted)
8989         {
8990           if (have_act)
8991             {
8992               *conflicted =
8993                  !svn_sqlite__column_is_null(stmt_act, 2); /* conflict_data */
8994             }
8995           else
8996             *conflicted = FALSE;
8997         }
8998
8999       if (lock)
9000         {
9001           if (op_depth != 0)
9002             *lock = NULL;
9003           else
9004             *lock = lock_from_columns(stmt_info, 17, 18, 19, 20, result_pool);
9005         }
9006
9007       if (have_work)
9008         *have_work = (op_depth != 0);
9009
9010       if (op_root)
9011         {
9012           *op_root = ((op_depth > 0)
9013                       && (op_depth == relpath_depth(local_relpath)));
9014         }
9015
9016       if (have_base || have_more_work)
9017         {
9018           if (have_more_work)
9019             *have_more_work = FALSE;
9020
9021           while (!err && op_depth != 0)
9022             {
9023               err = svn_sqlite__step(&have_info, stmt_info);
9024
9025               if (err || !have_info)
9026                 break;
9027
9028               op_depth = svn_sqlite__column_int(stmt_info, 0);
9029
9030               if (have_more_work)
9031                 {
9032                   if (op_depth > 0)
9033                     *have_more_work = TRUE;
9034
9035                   if (!have_base)
9036                    break;
9037                 }
9038             }
9039
9040           if (have_base)
9041             *have_base = (op_depth == 0);
9042         }
9043     }
9044   else if (have_act)
9045     {
9046       /* A row in ACTUAL_NODE should never exist without a corresponding
9047          node in BASE_NODE and/or WORKING_NODE unless it flags a tree conflict. */
9048       if (svn_sqlite__column_is_null(stmt_act, 2)) /* conflict_data */
9049           err = svn_error_createf(SVN_ERR_WC_CORRUPT, NULL,
9050                                   _("Corrupt data for '%s'"),
9051                                   path_for_error_message(wcroot, local_relpath,
9052                                                          scratch_pool));
9053       /* ### What should we return?  Should we have a separate
9054              function for reading actual-only nodes? */
9055
9056       /* As a safety measure, until we decide if we want to use
9057          read_info for actual-only nodes, make sure the caller asked
9058          for the conflict status. */
9059       SVN_ERR_ASSERT(conflicted);
9060
9061       if (status)
9062         *status = svn_wc__db_status_normal;  /* What! No it's not! */
9063       if (kind)
9064         *kind = svn_node_unknown;
9065       if (revision)
9066         *revision = SVN_INVALID_REVNUM;
9067       if (repos_relpath)
9068         *repos_relpath = NULL;
9069       if (repos_id)
9070         *repos_id = INVALID_REPOS_ID;
9071       if (changed_rev)
9072         *changed_rev = SVN_INVALID_REVNUM;
9073       if (changed_date)
9074         *changed_date = 0;
9075       if (depth)
9076         *depth = svn_depth_unknown;
9077       if (checksum)
9078         *checksum = NULL;
9079       if (target)
9080         *target = NULL;
9081       if (original_repos_relpath)
9082         *original_repos_relpath = NULL;
9083       if (original_repos_id)
9084         *original_repos_id = INVALID_REPOS_ID;
9085       if (original_revision)
9086         *original_revision = SVN_INVALID_REVNUM;
9087       if (lock)
9088         *lock = NULL;
9089       if (recorded_size)
9090         *recorded_size = 0;
9091       if (recorded_time)
9092         *recorded_time = 0;
9093       if (changelist)
9094         *changelist = svn_sqlite__column_text(stmt_act, 0, result_pool);
9095       if (op_root)
9096         *op_root = FALSE;
9097       if (had_props)
9098         *had_props = FALSE;
9099       if (props_mod)
9100         *props_mod = FALSE;
9101       if (conflicted)
9102         *conflicted = TRUE;
9103       if (have_base)
9104         *have_base = FALSE;
9105       if (have_more_work)
9106         *have_more_work = FALSE;
9107       if (have_work)
9108         *have_work = FALSE;
9109     }
9110   else
9111     {
9112       err = svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
9113                               _("The node '%s' was not found."),
9114                               path_for_error_message(wcroot, local_relpath,
9115                                                      scratch_pool));
9116     }
9117
9118   if (stmt_act != NULL)
9119     err = svn_error_compose_create(err, svn_sqlite__reset(stmt_act));
9120
9121   if (err && err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
9122     err = svn_error_quick_wrapf(err, _("Error reading node '%s'"),
9123                                 local_relpath);
9124
9125   SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt_info)));
9126
9127   return SVN_NO_ERROR;
9128 }
9129
9130
9131 svn_error_t *
9132 svn_wc__db_read_info_internal(svn_wc__db_status_t *status,
9133                               svn_node_kind_t *kind,
9134                               svn_revnum_t *revision,
9135                               const char **repos_relpath,
9136                               apr_int64_t *repos_id,
9137                               svn_revnum_t *changed_rev,
9138                               apr_time_t *changed_date,
9139                               const char **changed_author,
9140                               svn_depth_t *depth,
9141                               const svn_checksum_t **checksum,
9142                               const char **target,
9143                               const char **original_repos_relpath,
9144                               apr_int64_t *original_repos_id,
9145                               svn_revnum_t *original_revision,
9146                               svn_wc__db_lock_t **lock,
9147                               svn_filesize_t *recorded_size,
9148                               apr_time_t *recorded_time,
9149                               const char **changelist,
9150                               svn_boolean_t *conflicted,
9151                               svn_boolean_t *op_root,
9152                               svn_boolean_t *had_props,
9153                               svn_boolean_t *props_mod,
9154                               svn_boolean_t *have_base,
9155                               svn_boolean_t *have_more_work,
9156                               svn_boolean_t *have_work,
9157                               svn_wc__db_wcroot_t *wcroot,
9158                               const char *local_relpath,
9159                               apr_pool_t *result_pool,
9160                               apr_pool_t *scratch_pool)
9161 {
9162   return svn_error_trace(
9163            read_info(status, kind, revision, repos_relpath, repos_id,
9164                      changed_rev, changed_date, changed_author,
9165                      depth, checksum, target, original_repos_relpath,
9166                      original_repos_id, original_revision, lock,
9167                      recorded_size, recorded_time, changelist, conflicted,
9168                      op_root, had_props, props_mod,
9169                      have_base, have_more_work, have_work,
9170                      wcroot, local_relpath, result_pool, scratch_pool));
9171 }
9172
9173
9174 svn_error_t *
9175 svn_wc__db_read_info(svn_wc__db_status_t *status,
9176                      svn_node_kind_t *kind,
9177                      svn_revnum_t *revision,
9178                      const char **repos_relpath,
9179                      const char **repos_root_url,
9180                      const char **repos_uuid,
9181                      svn_revnum_t *changed_rev,
9182                      apr_time_t *changed_date,
9183                      const char **changed_author,
9184                      svn_depth_t *depth,
9185                      const svn_checksum_t **checksum,
9186                      const char **target,
9187                      const char **original_repos_relpath,
9188                      const char **original_root_url,
9189                      const char **original_uuid,
9190                      svn_revnum_t *original_revision,
9191                      svn_wc__db_lock_t **lock,
9192                      svn_filesize_t *recorded_size,
9193                      apr_time_t *recorded_time,
9194                      const char **changelist,
9195                      svn_boolean_t *conflicted,
9196                      svn_boolean_t *op_root,
9197                      svn_boolean_t *have_props,
9198                      svn_boolean_t *props_mod,
9199                      svn_boolean_t *have_base,
9200                      svn_boolean_t *have_more_work,
9201                      svn_boolean_t *have_work,
9202                      svn_wc__db_t *db,
9203                      const char *local_abspath,
9204                      apr_pool_t *result_pool,
9205                      apr_pool_t *scratch_pool)
9206 {
9207   svn_wc__db_wcroot_t *wcroot;
9208   const char *local_relpath;
9209   apr_int64_t repos_id, original_repos_id;
9210
9211   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9212
9213   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9214                               local_abspath, scratch_pool, scratch_pool));
9215   VERIFY_USABLE_WCROOT(wcroot);
9216
9217   SVN_WC__DB_WITH_TXN4(
9218           read_info(status, kind, revision, repos_relpath, &repos_id,
9219                     changed_rev, changed_date, changed_author,
9220                     depth, checksum, target, original_repos_relpath,
9221                     &original_repos_id, original_revision, lock,
9222                     recorded_size, recorded_time, changelist, conflicted,
9223                     op_root, have_props, props_mod,
9224                     have_base, have_more_work, have_work,
9225                     wcroot, local_relpath, result_pool, scratch_pool),
9226           svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid,
9227                                       wcroot, repos_id, result_pool),
9228           svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
9229                                       wcroot, original_repos_id,
9230                                       result_pool),
9231         SVN_NO_ERROR,
9232         wcroot);
9233
9234   return SVN_NO_ERROR;
9235 }
9236
9237 static svn_error_t *
9238 is_wclocked(svn_boolean_t *locked,
9239             svn_wc__db_wcroot_t *wcroot,
9240             const char *dir_relpath,
9241             apr_pool_t *scratch_pool);
9242
9243 /* Helper for read_children_info and single variant */
9244 static svn_error_t *
9245 find_conflict_descendants(svn_boolean_t *conflict_exists,
9246                           svn_wc__db_wcroot_t *wcroot,
9247                           const char *local_relpath,
9248                           apr_pool_t *scratch_pool)
9249 {
9250   svn_sqlite__stmt_t *stmt;
9251
9252   /* Only used on files, so certainly not wcroot*/
9253   assert(local_relpath[0] != '\0');
9254
9255   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9256                                     STMT_FIND_CONFLICT_DESCENDANT));
9257
9258   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9259   SVN_ERR(svn_sqlite__step(conflict_exists, stmt));
9260
9261   return svn_error_trace(svn_sqlite__reset(stmt));
9262 }
9263
9264 /* What we really want to store about a node.  This relies on the
9265    offset of svn_wc__db_info_t being zero. */
9266 struct read_children_info_item_t
9267 {
9268   struct svn_wc__db_info_t info;
9269   int op_depth;
9270   int nr_layers;
9271   svn_boolean_t was_dir;
9272 };
9273
9274 /* Implementation of svn_wc__db_read_children_info */
9275 static svn_error_t *
9276 read_children_info(svn_wc__db_wcroot_t *wcroot,
9277                    const char *dir_relpath,
9278                    apr_hash_t *conflicts,
9279                    apr_hash_t *nodes,
9280                    svn_boolean_t base_tree_only,
9281                    apr_pool_t *result_pool,
9282                    apr_pool_t *scratch_pool)
9283 {
9284   svn_sqlite__stmt_t *stmt;
9285   svn_boolean_t have_row;
9286   const char *repos_root_url = NULL;
9287   const char *repos_uuid = NULL;
9288   apr_int64_t last_repos_id = INVALID_REPOS_ID;
9289   const char *last_repos_root_url = NULL;
9290
9291   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9292                                     (base_tree_only
9293                                      ? STMT_SELECT_BASE_NODE_CHILDREN_INFO
9294                                      : STMT_SELECT_NODE_CHILDREN_INFO)));
9295   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9296   SVN_ERR(svn_sqlite__step(&have_row, stmt));
9297
9298   while (have_row)
9299     {
9300       /* CHILD item points to what we have about the node. We only provide
9301          CHILD->item to our caller. */
9302       struct read_children_info_item_t *child_item;
9303       const char *child_relpath = svn_sqlite__column_text(stmt, 19, NULL);
9304       const char *name = svn_relpath_basename(child_relpath, NULL);
9305       svn_error_t *err;
9306       int op_depth;
9307       svn_boolean_t new_child;
9308
9309       child_item = (base_tree_only ? NULL : svn_hash_gets(nodes, name));
9310       if (child_item)
9311         new_child = FALSE;
9312       else
9313         {
9314           child_item = apr_pcalloc(result_pool, sizeof(*child_item));
9315           new_child = TRUE;
9316         }
9317
9318       op_depth = svn_sqlite__column_int(stmt, 0);
9319
9320       /* Do we have new or better information? */
9321       if (new_child)
9322         {
9323           struct svn_wc__db_info_t *child = &child_item->info;
9324           child_item->op_depth = op_depth;
9325
9326           child->kind = svn_sqlite__column_token(stmt, 4, kind_map);
9327
9328           child->status = svn_sqlite__column_token(stmt, 3, presence_map);
9329           if (op_depth != 0)
9330             {
9331               if (child->status == svn_wc__db_status_incomplete)
9332                 child->incomplete = TRUE;
9333               err = convert_to_working_status(&child->status, child->status);
9334               if (err)
9335                 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9336             }
9337
9338           if (op_depth != 0)
9339             child->revnum = SVN_INVALID_REVNUM;
9340           else
9341             child->revnum = svn_sqlite__column_revnum(stmt, 5);
9342
9343           if (op_depth != 0)
9344             child->repos_relpath = NULL;
9345           else
9346             child->repos_relpath = svn_sqlite__column_text(stmt, 2,
9347                                                            result_pool);
9348
9349           if (op_depth != 0 || svn_sqlite__column_is_null(stmt, 1))
9350             {
9351               child->repos_root_url = NULL;
9352               child->repos_uuid = NULL;
9353             }
9354           else
9355             {
9356               apr_int64_t repos_id = svn_sqlite__column_int64(stmt, 1);
9357               if (!repos_root_url ||
9358                   (last_repos_id != INVALID_REPOS_ID &&
9359                    repos_id != last_repos_id))
9360                 {
9361                   last_repos_root_url = repos_root_url;
9362                   err = svn_wc__db_fetch_repos_info(&repos_root_url,
9363                                                     &repos_uuid,
9364                                                     wcroot, repos_id,
9365                                                     result_pool);
9366                   if (err)
9367                     SVN_ERR(svn_error_compose_create(err,
9368                                                  svn_sqlite__reset(stmt)));
9369                 }
9370
9371               if (last_repos_id == INVALID_REPOS_ID)
9372                 last_repos_id = repos_id;
9373
9374               /* Assume working copy is all one repos_id so that a
9375                  single cached value is sufficient. */
9376               if (repos_id != last_repos_id)
9377                 {
9378                   err= svn_error_createf(
9379                          SVN_ERR_WC_DB_ERROR, NULL,
9380                          _("The node '%s' comes from unexpected repository "
9381                            "'%s', expected '%s'; if this node is a file "
9382                            "external using the correct URL in the external "
9383                            "definition can fix the problem, see issue #4087"),
9384                          child_relpath, repos_root_url, last_repos_root_url);
9385                   return svn_error_compose_create(err, svn_sqlite__reset(stmt));
9386                 }
9387               child->repos_root_url = repos_root_url;
9388               child->repos_uuid = repos_uuid;
9389             }
9390
9391           child->changed_rev = svn_sqlite__column_revnum(stmt, 8);
9392
9393           child->changed_date = svn_sqlite__column_int64(stmt, 9);
9394
9395           child->changed_author = svn_sqlite__column_text(stmt, 10,
9396                                                           result_pool);
9397
9398           if (child->kind != svn_node_dir)
9399             child->depth = svn_depth_unknown;
9400           else
9401             {
9402               child->has_descendants = TRUE;
9403               child_item->was_dir = TRUE;
9404               child->depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
9405                                                            svn_depth_unknown);
9406               if (new_child)
9407                 {
9408                   err = is_wclocked(&child->locked, wcroot, child_relpath,
9409                                     scratch_pool);
9410
9411                   if (err)
9412                     SVN_ERR(svn_error_compose_create(err,
9413                                                      svn_sqlite__reset(stmt)));
9414                 }
9415             }
9416
9417           child->recorded_time = svn_sqlite__column_int64(stmt, 13);
9418           child->recorded_size = get_recorded_size(stmt, 7);
9419           child->has_checksum = !svn_sqlite__column_is_null(stmt, 6);
9420           child->copied = op_depth > 0 && !svn_sqlite__column_is_null(stmt, 2);
9421           child->had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
9422 #ifdef HAVE_SYMLINK
9423           if (child->had_props)
9424             {
9425               apr_hash_t *properties;
9426               err = svn_sqlite__column_properties(&properties, stmt, 14,
9427                                                   scratch_pool, scratch_pool);
9428               if (err)
9429                 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9430
9431               child->special = (child->had_props
9432                                 && svn_hash_gets(properties, SVN_PROP_SPECIAL));
9433             }
9434 #endif
9435           if (op_depth == 0)
9436             child->op_root = FALSE;
9437           else
9438             child->op_root = (op_depth == relpath_depth(child_relpath));
9439
9440           if (op_depth && child->op_root)
9441             child_item->info.moved_here = svn_sqlite__column_boolean(stmt, 20);
9442
9443           if (new_child)
9444             svn_hash_sets(nodes, apr_pstrdup(result_pool, name), child);
9445         }
9446       else if (!child_item->was_dir
9447                && svn_sqlite__column_token(stmt, 4, kind_map) == svn_node_dir)
9448         {
9449           child_item->was_dir = TRUE;
9450
9451           err = find_conflict_descendants(&child_item->info.has_descendants,
9452                                           wcroot, child_relpath,
9453                                           scratch_pool);
9454           if (err)
9455             SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9456         }
9457
9458       if (op_depth == 0)
9459         {
9460           child_item->info.have_base = TRUE;
9461
9462           /* Get the lock info, available only at op_depth 0. */
9463           child_item->info.lock = lock_from_columns(stmt, 15, 16, 17, 18,
9464                                                     result_pool);
9465
9466           /* FILE_EXTERNAL flag only on op_depth 0. */
9467           child_item->info.file_external = svn_sqlite__column_boolean(stmt,
9468                                                                       22);
9469         }
9470       else
9471         {
9472           const char *moved_to_relpath;
9473
9474           child_item->nr_layers++;
9475           child_item->info.have_more_work = (child_item->nr_layers > 1);
9476
9477
9478           /* A local_relpath can be moved multiple times at different op
9479              depths and it really depends on the caller what is interesting.
9480              We provide a simple linked list with the moved_from information */
9481
9482           moved_to_relpath = svn_sqlite__column_text(stmt, 21, NULL);
9483           if (moved_to_relpath)
9484             {
9485               struct svn_wc__db_moved_to_info_t *moved_to;
9486               struct svn_wc__db_moved_to_info_t **next;
9487               const char *shadow_op_relpath;
9488
9489               moved_to = apr_pcalloc(result_pool, sizeof(*moved_to));
9490               moved_to->moved_to_abspath = svn_dirent_join(wcroot->abspath,
9491                                                            moved_to_relpath,
9492                                                            result_pool);
9493
9494               shadow_op_relpath = svn_relpath_prefix(child_relpath, op_depth,
9495                                                      scratch_pool);
9496
9497               moved_to->shadow_op_root_abspath =
9498                         svn_dirent_join(wcroot->abspath, shadow_op_relpath,
9499                                         result_pool);
9500
9501               next = &child_item->info.moved_to;
9502
9503               while (*next &&
9504                      0 < strcmp((*next)->shadow_op_root_abspath,
9505                                 moved_to->shadow_op_root_abspath))
9506                 next = &((*next)->next);
9507
9508               moved_to->next = *next;
9509               *next = moved_to;
9510             }
9511         }
9512
9513       SVN_ERR(svn_sqlite__step(&have_row, stmt));
9514     }
9515
9516   SVN_ERR(svn_sqlite__reset(stmt));
9517
9518   if (!base_tree_only)
9519     {
9520       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9521                                         STMT_SELECT_ACTUAL_CHILDREN_INFO));
9522       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9523       SVN_ERR(svn_sqlite__step(&have_row, stmt));
9524
9525       while (have_row)
9526         {
9527           struct read_children_info_item_t *child_item;
9528           struct svn_wc__db_info_t *child;
9529           const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9530           const char *name = svn_relpath_basename(child_relpath, NULL);
9531
9532           child_item = svn_hash_gets(nodes, name);
9533           if (!child_item)
9534             {
9535               child_item = apr_pcalloc(result_pool, sizeof(*child_item));
9536               child_item->info.status = svn_wc__db_status_not_present;
9537             }
9538
9539           child = &child_item->info;
9540
9541           child->changelist = svn_sqlite__column_text(stmt, 1, result_pool);
9542
9543           child->props_mod = !svn_sqlite__column_is_null(stmt, 2);
9544 #ifdef HAVE_SYMLINK
9545           if (child->props_mod)
9546             {
9547               svn_error_t *err;
9548               apr_hash_t *properties;
9549
9550               err = svn_sqlite__column_properties(&properties, stmt, 2,
9551                                                   scratch_pool, scratch_pool);
9552               if (err)
9553                 SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
9554               child->special = (NULL != svn_hash_gets(properties,
9555                                                       SVN_PROP_SPECIAL));
9556             }
9557 #endif
9558
9559           /* conflict */
9560           child->conflicted = !svn_sqlite__column_is_null(stmt, 3);
9561
9562           if (child->conflicted)
9563             svn_hash_sets(conflicts, apr_pstrdup(result_pool, name), "");
9564
9565           SVN_ERR(svn_sqlite__step(&have_row, stmt));
9566         }
9567
9568       SVN_ERR(svn_sqlite__reset(stmt));
9569     }
9570
9571   return SVN_NO_ERROR;
9572 }
9573
9574 svn_error_t *
9575 svn_wc__db_read_children_info(apr_hash_t **nodes,
9576                               apr_hash_t **conflicts,
9577                               svn_wc__db_t *db,
9578                               const char *dir_abspath,
9579                               svn_boolean_t base_tree_only,
9580                               apr_pool_t *result_pool,
9581                               apr_pool_t *scratch_pool)
9582 {
9583   svn_wc__db_wcroot_t *wcroot;
9584   const char *dir_relpath;
9585
9586   *conflicts = apr_hash_make(result_pool);
9587   *nodes = apr_hash_make(result_pool);
9588   SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9589
9590   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9591                                                 dir_abspath,
9592                                                 scratch_pool, scratch_pool));
9593   VERIFY_USABLE_WCROOT(wcroot);
9594
9595   SVN_WC__DB_WITH_TXN(
9596     read_children_info(wcroot, dir_relpath, *conflicts, *nodes,
9597                        base_tree_only, result_pool, scratch_pool),
9598     wcroot);
9599
9600   return SVN_NO_ERROR;
9601 }
9602
9603 /* Implementation of svn_wc__db_read_single_info.
9604
9605    ### This function is very similar to a lot of code inside
9606    read_children_info, but that performs some tricks to re-use data between
9607    different siblings.
9608
9609    ### We read the same few NODES records a few times via different helper
9610    functions, so this could be optimized bit, but everything is within
9611    a sqlite transaction and all queries are backed by an index, so generally
9612    everything (including the used indexes) should be in the sqlite page cache
9613    after the first query.
9614 */
9615 static svn_error_t *
9616 read_single_info(const struct svn_wc__db_info_t **info,
9617                  svn_wc__db_wcroot_t *wcroot,
9618                  const char *local_relpath,
9619                  svn_boolean_t base_tree_only,
9620                  apr_pool_t *result_pool,
9621                  apr_pool_t *scratch_pool)
9622 {
9623   struct svn_wc__db_info_t *mtb;
9624   apr_int64_t repos_id;
9625   const svn_checksum_t *checksum;
9626   const char *original_repos_relpath;
9627   svn_boolean_t have_work;
9628   apr_hash_t *properties;
9629
9630   mtb = apr_pcalloc(result_pool, sizeof(*mtb));
9631
9632   if (!base_tree_only)
9633     SVN_ERR(read_info(&mtb->status, &mtb->kind, &mtb->revnum,
9634                       &mtb->repos_relpath, &repos_id, &mtb->changed_rev,
9635                       &mtb->changed_date, &mtb->changed_author, &mtb->depth,
9636                       &checksum, NULL, &original_repos_relpath, NULL, NULL,
9637                       &mtb->lock, &mtb->recorded_size, &mtb->recorded_time,
9638                       &mtb->changelist, &mtb->conflicted, &mtb->op_root,
9639                       &mtb->had_props, &mtb->props_mod, &mtb->have_base,
9640                       &mtb->have_more_work, &have_work,
9641                       wcroot, local_relpath, result_pool, scratch_pool));
9642   else
9643     {
9644       svn_boolean_t update_root;
9645
9646       have_work = FALSE;
9647       original_repos_relpath = NULL;
9648
9649       SVN_ERR(svn_wc__db_base_get_info_internal(
9650                   &mtb->status, &mtb->kind, &mtb->revnum, &mtb->repos_relpath,
9651                   &repos_id, &mtb->changed_rev, &mtb->changed_date,
9652                   &mtb->changed_author, &mtb->depth, &checksum, NULL,
9653                   &mtb->lock, &mtb->had_props, &properties, &update_root,
9654                   wcroot, local_relpath, scratch_pool, scratch_pool));
9655
9656       mtb->have_base = TRUE;
9657       mtb->file_external = (update_root && mtb->kind == svn_node_file);
9658     }
9659
9660   /* Query the same rows in the database again for move information */
9661   if (have_work && (mtb->have_base || mtb->have_more_work))
9662     {
9663       svn_sqlite__stmt_t *stmt;
9664       svn_boolean_t have_row;
9665
9666       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9667                                         STMT_SELECT_MOVED_TO_NODE));
9668       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9669
9670       SVN_ERR(svn_sqlite__step(&have_row, stmt));
9671
9672       while (have_row)
9673         {
9674           struct svn_wc__db_moved_to_info_t *move;
9675           int op_depth = svn_sqlite__column_int(stmt, 0);
9676           const char *moved_to_relpath = svn_sqlite__column_text(stmt, 1, NULL);
9677           const char *cur_relpath;
9678
9679           move = apr_pcalloc(result_pool, sizeof(*move));
9680           move->moved_to_abspath = svn_dirent_join(wcroot->abspath,
9681                                                    moved_to_relpath,
9682                                                    result_pool);
9683
9684           cur_relpath = svn_relpath_prefix(local_relpath, op_depth,
9685                                            scratch_pool);
9686
9687           move->shadow_op_root_abspath = svn_dirent_join(wcroot->abspath,
9688                                                          cur_relpath,
9689                                                          result_pool);
9690
9691           move->next = mtb->moved_to;
9692           mtb->moved_to = move;
9693
9694           SVN_ERR(svn_sqlite__step(&have_row, stmt));
9695         }
9696
9697       SVN_ERR(svn_sqlite__reset(stmt));
9698     }
9699
9700   /* Maybe we have to get some shadowed lock from BASE to make our test suite
9701      happy... (It might be completely unrelated, but...)
9702      This queries the same BASE row again, joined to the lock table */
9703   if (!base_tree_only && mtb->have_base
9704       && (have_work || mtb->kind == svn_node_file))
9705     {
9706       svn_boolean_t update_root;
9707       svn_wc__db_lock_t **lock_arg = NULL;
9708
9709       if (have_work)
9710         lock_arg = &mtb->lock;
9711
9712       SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL, NULL,
9713                                                 NULL, NULL, NULL, NULL, NULL,
9714                                                 NULL, lock_arg, NULL, NULL,
9715                                                 &update_root,
9716                                                 wcroot, local_relpath,
9717                                                 result_pool, scratch_pool));
9718
9719       mtb->file_external = (update_root && mtb->kind == svn_node_file);
9720     }
9721
9722   if (mtb->status == svn_wc__db_status_added)
9723     {
9724       svn_wc__db_status_t status;
9725
9726       SVN_ERR(scan_addition(&status, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
9727                             NULL, NULL,
9728                             wcroot, local_relpath,
9729                             result_pool, scratch_pool));
9730
9731       mtb->moved_here = (status == svn_wc__db_status_moved_here);
9732       mtb->incomplete = (status == svn_wc__db_status_incomplete);
9733     }
9734
9735 #ifdef HAVE_SYMLINK
9736   if (mtb->kind == svn_node_file
9737       && (mtb->had_props || mtb->props_mod
9738           || (base_tree_only && properties)))
9739     {
9740       if (!base_tree_only)
9741         {
9742           if (mtb->props_mod)
9743             SVN_ERR(svn_wc__db_read_props_internal(&properties,
9744                                                    wcroot, local_relpath,
9745                                                    scratch_pool, scratch_pool));
9746           else
9747             SVN_ERR(db_read_pristine_props(&properties, wcroot, local_relpath,
9748                                            TRUE /* deleted_ok */,
9749                                            scratch_pool, scratch_pool));
9750         }
9751
9752       mtb->special = (NULL != svn_hash_gets(properties, SVN_PROP_SPECIAL));
9753     }
9754 #endif
9755
9756   mtb->has_checksum = (checksum != NULL);
9757   mtb->copied = (original_repos_relpath != NULL);
9758
9759   SVN_ERR(svn_wc__db_fetch_repos_info(&mtb->repos_root_url, &mtb->repos_uuid,
9760                                       wcroot, repos_id, result_pool));
9761
9762   if (!base_tree_only && mtb->kind == svn_node_dir)
9763     SVN_ERR(is_wclocked(&mtb->locked, wcroot, local_relpath, scratch_pool));
9764
9765   if (mtb->kind == svn_node_dir)
9766     mtb->has_descendants = TRUE;
9767   else
9768     SVN_ERR(find_conflict_descendants(&mtb->has_descendants,
9769                                       wcroot, local_relpath, scratch_pool));
9770
9771   *info = mtb;
9772
9773   return SVN_NO_ERROR;
9774 }
9775
9776 svn_error_t *
9777 svn_wc__db_read_single_info(const struct svn_wc__db_info_t **info,
9778                             svn_wc__db_t *db,
9779                             const char *local_abspath,
9780                             svn_boolean_t base_tree_only,
9781                             apr_pool_t *result_pool,
9782                             apr_pool_t *scratch_pool)
9783 {
9784   svn_wc__db_wcroot_t *wcroot;
9785   const char *local_relpath;
9786
9787   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9788
9789   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9790                                                 local_abspath,
9791                                                 scratch_pool, scratch_pool));
9792   VERIFY_USABLE_WCROOT(wcroot);
9793
9794   SVN_WC__DB_WITH_TXN(read_single_info(info, wcroot, local_relpath,
9795                                        base_tree_only,
9796                                        result_pool, scratch_pool),
9797                       wcroot);
9798
9799   return SVN_NO_ERROR;
9800 }
9801
9802 svn_error_t *
9803 svn_wc__db_read_pristine_info(svn_wc__db_status_t *status,
9804                               svn_node_kind_t *kind,
9805                               svn_revnum_t *changed_rev,
9806                               apr_time_t *changed_date,
9807                               const char **changed_author,
9808                               svn_depth_t *depth,  /* dirs only */
9809                               const svn_checksum_t **checksum, /* files only */
9810                               const char **target, /* symlinks only */
9811                               svn_boolean_t *had_props,
9812                               apr_hash_t **props,
9813                               svn_wc__db_t *db,
9814                               const char *local_abspath,
9815                               apr_pool_t *result_pool,
9816                               apr_pool_t *scratch_pool)
9817 {
9818   svn_wc__db_wcroot_t *wcroot;
9819   const char *local_relpath;
9820   svn_sqlite__stmt_t *stmt;
9821   svn_boolean_t have_row;
9822   svn_error_t *err = NULL;
9823   int op_depth;
9824   svn_wc__db_status_t raw_status;
9825   svn_node_kind_t node_kind;
9826
9827   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
9828
9829   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
9830                                                 local_abspath,
9831                                                 scratch_pool, scratch_pool));
9832   VERIFY_USABLE_WCROOT(wcroot);
9833
9834   /* Obtain the most likely to exist record first, to make sure we don't
9835      have to obtain the SQLite read-lock multiple times */
9836   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9837                                     STMT_SELECT_NODE_INFO));
9838   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
9839   SVN_ERR(svn_sqlite__step(&have_row, stmt));
9840
9841   if (!have_row)
9842     {
9843       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
9844                                svn_sqlite__reset(stmt),
9845                                _("The node '%s' was not found."),
9846                                path_for_error_message(wcroot,
9847                                                       local_relpath,
9848                                                       scratch_pool));
9849     }
9850
9851   op_depth = svn_sqlite__column_int(stmt, 0);
9852   raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9853
9854   if (op_depth > 0 && raw_status == svn_wc__db_status_base_deleted)
9855     {
9856       SVN_ERR(svn_sqlite__step_row(stmt));
9857
9858       op_depth = svn_sqlite__column_int(stmt, 0);
9859       raw_status = svn_sqlite__column_token(stmt, 3, presence_map);
9860     }
9861
9862   node_kind = svn_sqlite__column_token(stmt, 4, kind_map);
9863
9864   if (status)
9865     {
9866       if (op_depth > 0)
9867         {
9868           err = svn_error_compose_create(err,
9869                                          convert_to_working_status(
9870                                                     status,
9871                                                     raw_status));
9872         }
9873       else
9874         *status = raw_status;
9875     }
9876   if (kind)
9877     {
9878       *kind = node_kind;
9879     }
9880   if (changed_rev)
9881     {
9882       *changed_rev = svn_sqlite__column_revnum(stmt, 8);
9883     }
9884   if (changed_date)
9885     {
9886       *changed_date = svn_sqlite__column_int64(stmt, 9);
9887     }
9888   if (changed_author)
9889     {
9890       *changed_author = svn_sqlite__column_text(stmt, 10,
9891                                                 result_pool);
9892     }
9893   if (depth)
9894     {
9895       if (node_kind != svn_node_dir)
9896         {
9897           *depth = svn_depth_unknown;
9898         }
9899       else
9900         {
9901           *depth = svn_sqlite__column_token_null(stmt, 11, depth_map,
9902                                                  svn_depth_unknown);
9903         }
9904     }
9905   if (checksum)
9906     {
9907       if (node_kind != svn_node_file)
9908         {
9909           *checksum = NULL;
9910         }
9911       else
9912         {
9913           svn_error_t *err2;
9914           err2 = svn_sqlite__column_checksum(checksum, stmt, 6, result_pool);
9915
9916           if (err2 != NULL)
9917             {
9918               if (err)
9919                 err = svn_error_compose_create(
9920                          err,
9921                          svn_error_createf(
9922                                err->apr_err, err2,
9923                               _("The node '%s' has a corrupt checksum value."),
9924                               path_for_error_message(wcroot, local_relpath,
9925                                                      scratch_pool)));
9926               else
9927                 err = err2;
9928             }
9929         }
9930     }
9931   if (target)
9932     {
9933       if (node_kind != svn_node_symlink)
9934         *target = NULL;
9935       else
9936         *target = svn_sqlite__column_text(stmt, 12, result_pool);
9937     }
9938   if (had_props)
9939     {
9940       *had_props = SQLITE_PROPERTIES_AVAILABLE(stmt, 14);
9941     }
9942   if (props)
9943     {
9944       if (raw_status == svn_wc__db_status_normal
9945           || raw_status == svn_wc__db_status_incomplete)
9946         {
9947           SVN_ERR(svn_sqlite__column_properties(props, stmt, 14,
9948                                                 result_pool, scratch_pool));
9949           if (*props == NULL)
9950             *props = apr_hash_make(result_pool);
9951         }
9952       else
9953         {
9954           assert(svn_sqlite__column_is_null(stmt, 14));
9955           *props = NULL;
9956         }
9957     }
9958
9959   return svn_error_trace(
9960             svn_error_compose_create(err,
9961                                      svn_sqlite__reset(stmt)));
9962 }
9963
9964 svn_error_t *
9965 svn_wc__db_read_children_walker_info(const apr_array_header_t **items,
9966                                      svn_wc__db_t *db,
9967                                      const char *dir_abspath,
9968                                      apr_pool_t *result_pool,
9969                                      apr_pool_t *scratch_pool)
9970 {
9971   svn_wc__db_wcroot_t *wcroot;
9972   const char *dir_relpath;
9973   svn_sqlite__stmt_t *stmt;
9974   svn_boolean_t have_row;
9975   apr_array_header_t *nodes;
9976
9977   SVN_ERR_ASSERT(svn_dirent_is_absolute(dir_abspath));
9978
9979   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &dir_relpath, db,
9980                                              dir_abspath,
9981                                              scratch_pool, scratch_pool));
9982   VERIFY_USABLE_WCROOT(wcroot);
9983
9984   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
9985                                     STMT_SELECT_NODE_CHILDREN_WALKER_INFO));
9986   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, dir_relpath));
9987   SVN_ERR(svn_sqlite__step(&have_row, stmt));
9988
9989   nodes = apr_array_make(result_pool, 16,
9990                           sizeof(struct svn_wc__db_walker_info_t *));
9991   while (have_row)
9992     {
9993       struct svn_wc__db_walker_info_t *child;
9994       const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
9995       const char *name = svn_relpath_basename(child_relpath, result_pool);
9996       int op_depth = svn_sqlite__column_int(stmt, 1);
9997       svn_error_t *err;
9998
9999       child = apr_palloc(result_pool, sizeof(*child));
10000       child->name = name;
10001       child->status = svn_sqlite__column_token(stmt, 2, presence_map);
10002       if (op_depth > 0)
10003         {
10004           err = convert_to_working_status(&child->status, child->status);
10005           if (err)
10006             SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10007         }
10008       child->kind = svn_sqlite__column_token(stmt, 3, kind_map);
10009
10010       APR_ARRAY_PUSH(nodes, struct svn_wc__db_walker_info_t *) = child;
10011
10012       SVN_ERR(svn_sqlite__step(&have_row, stmt));
10013     }
10014
10015   SVN_ERR(svn_sqlite__reset(stmt));
10016
10017   *items = nodes;
10018
10019   return SVN_NO_ERROR;
10020 }
10021
10022 svn_error_t *
10023 svn_wc__db_read_node_install_info(const char **wcroot_abspath,
10024                                   const svn_checksum_t **sha1_checksum,
10025                                   apr_hash_t **pristine_props,
10026                                   apr_time_t *changed_date,
10027                                   svn_wc__db_t *db,
10028                                   const char *local_abspath,
10029                                   const char *wri_abspath,
10030                                   apr_pool_t *result_pool,
10031                                   apr_pool_t *scratch_pool)
10032 {
10033   svn_wc__db_wcroot_t *wcroot;
10034   const char *local_relpath;
10035   svn_sqlite__stmt_t *stmt;
10036   svn_error_t *err = NULL;
10037   svn_boolean_t have_row;
10038
10039   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10040
10041   if (!wri_abspath)
10042     wri_abspath = local_abspath;
10043
10044   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10045                               wri_abspath, scratch_pool, scratch_pool));
10046   VERIFY_USABLE_WCROOT(wcroot);
10047
10048   if (local_abspath != wri_abspath
10049       && strcmp(local_abspath, wri_abspath))
10050     {
10051       if (!svn_dirent_is_ancestor(wcroot->abspath, local_abspath))
10052         return svn_error_createf(
10053                     SVN_ERR_WC_PATH_NOT_FOUND, NULL,
10054                     _("The node '%s' is not in working copy '%s'"),
10055                     svn_dirent_local_style(local_abspath, scratch_pool),
10056                     svn_dirent_local_style(wcroot->abspath, scratch_pool));
10057
10058       local_relpath = svn_dirent_skip_ancestor(wcroot->abspath, local_abspath);
10059     }
10060
10061   if (wcroot_abspath != NULL)
10062     *wcroot_abspath = apr_pstrdup(result_pool, wcroot->abspath);
10063
10064   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10065                                     STMT_SELECT_NODE_INFO));
10066
10067   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10068
10069   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10070
10071   if (have_row)
10072     {
10073       if (sha1_checksum)
10074         err = svn_sqlite__column_checksum(sha1_checksum, stmt, 6, result_pool);
10075
10076       if (!err && pristine_props)
10077         {
10078           err = svn_sqlite__column_properties(pristine_props, stmt, 14,
10079                                               result_pool, scratch_pool);
10080           /* Null means no props (assuming presence normal or incomplete). */
10081           if (*pristine_props == NULL)
10082             *pristine_props = apr_hash_make(result_pool);
10083         }
10084
10085       if (changed_date)
10086         *changed_date = svn_sqlite__column_int64(stmt, 9);
10087     }
10088   else
10089     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10090                              svn_sqlite__reset(stmt),
10091                              _("The node '%s' is not installable"),
10092                              svn_dirent_local_style(local_abspath,
10093                                                     scratch_pool));
10094
10095   SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10096
10097   return SVN_NO_ERROR;
10098 }
10099
10100
10101
10102 /* The body of svn_wc__db_read_repos_info().
10103  */
10104 static svn_error_t *
10105 db_read_repos_info(svn_revnum_t *revision,
10106                    const char **repos_relpath,
10107                    apr_int64_t *repos_id,
10108                    svn_wc__db_wcroot_t *wcroot,
10109                    const char *local_relpath,
10110                    apr_pool_t *result_pool,
10111                    apr_pool_t *scratch_pool)
10112 {
10113   svn_wc__db_status_t status;
10114
10115   SVN_ERR(read_info(&status, NULL, revision, repos_relpath, repos_id, NULL,
10116                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10117                     NULL, NULL, NULL, NULL, NULL, NULL, NULL,
10118                     NULL, NULL, NULL,
10119                     wcroot, local_relpath, result_pool, scratch_pool));
10120
10121   if ((repos_relpath && !*repos_relpath)
10122       || (repos_id && *repos_id == INVALID_REPOS_ID))
10123     {
10124       if (status == svn_wc__db_status_added)
10125         {
10126           SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL,
10127                                 NULL, NULL, NULL, NULL, NULL,
10128                                 wcroot, local_relpath,
10129                                 result_pool, scratch_pool));
10130         }
10131       else if (status == svn_wc__db_status_deleted)
10132         {
10133           const char *base_del_relpath;
10134           const char *work_del_relpath;
10135
10136           SVN_ERR(scan_deletion(&base_del_relpath, NULL,
10137                                 &work_del_relpath,
10138                                 NULL, wcroot,
10139                                 local_relpath,
10140                                 scratch_pool,
10141                                 scratch_pool));
10142
10143           if (work_del_relpath)
10144             {
10145               /* The parent of the WORKING delete, must be an addition */
10146               const char *work_relpath = NULL;
10147
10148               /* work_del_relpath should not be NULL. However, we have
10149                * observed instances where that assumption was not met.
10150                * Bail out in that case instead of crashing with a segfault.
10151                */
10152               SVN_ERR_ASSERT(work_del_relpath != NULL);
10153               work_relpath = svn_relpath_dirname(work_del_relpath,
10154                                                  scratch_pool);
10155
10156               SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id,
10157                                     NULL, NULL, NULL, NULL, NULL, NULL,
10158                                     wcroot, work_relpath,
10159                                     scratch_pool, scratch_pool));
10160
10161               if (repos_relpath)
10162                 *repos_relpath = svn_relpath_join(
10163                                     *repos_relpath,
10164                                     svn_dirent_skip_ancestor(work_relpath,
10165                                                              local_relpath),
10166                                     result_pool);
10167             }
10168           else if (base_del_relpath)
10169             {
10170               SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, revision,
10171                                                         repos_relpath,
10172                                                         repos_id,
10173                                                         NULL, NULL, NULL,
10174                                                         NULL, NULL, NULL,
10175                                                         NULL, NULL, NULL, NULL,
10176                                                         wcroot,
10177                                                         base_del_relpath,
10178                                                         scratch_pool,
10179                                                         scratch_pool));
10180
10181               if (repos_relpath)
10182                 *repos_relpath = svn_relpath_join(
10183                                     *repos_relpath,
10184                                     svn_dirent_skip_ancestor(base_del_relpath,
10185                                                              local_relpath),
10186                                     result_pool);
10187             }
10188           else
10189             SVN_ERR_MALFUNCTION();
10190         }
10191       else if (status == svn_wc__db_status_excluded)
10192         {
10193           const char *parent_relpath;
10194           const char *name;
10195
10196           /* A BASE excluded would have had repository information, so
10197              we have a working exclude, which must be below an addition */
10198
10199           svn_relpath_split(&parent_relpath, &name, local_relpath,
10200                             scratch_pool);
10201           SVN_ERR(scan_addition(NULL, NULL, repos_relpath, repos_id, NULL,
10202                                 NULL, NULL, NULL, NULL, NULL,
10203                                 wcroot, parent_relpath,
10204                                 scratch_pool, scratch_pool));
10205
10206           if (repos_relpath)
10207             *repos_relpath = svn_relpath_join(*repos_relpath, name,
10208                                               result_pool);
10209
10210           return SVN_NO_ERROR;
10211         }
10212       else
10213         {
10214           /* All working statee are explicitly handled and all base statee
10215              have a repos_relpath */
10216           SVN_ERR_MALFUNCTION();
10217         }
10218     }
10219
10220   return SVN_NO_ERROR;
10221 }
10222
10223
10224 svn_error_t *
10225 svn_wc__db_read_repos_info(svn_revnum_t *revision,
10226                            const char **repos_relpath,
10227                            const char **repos_root_url,
10228                            const char **repos_uuid,
10229                            svn_wc__db_t *db,
10230                            const char *local_abspath,
10231                            apr_pool_t *result_pool,
10232                            apr_pool_t *scratch_pool)
10233 {
10234   svn_wc__db_wcroot_t *wcroot;
10235   const char *local_relpath;
10236   apr_int64_t repos_id = INVALID_REPOS_ID;
10237
10238   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10239
10240   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10241                                                 local_abspath,
10242                                                 scratch_pool, scratch_pool));
10243   VERIFY_USABLE_WCROOT(wcroot);
10244
10245   SVN_WC__DB_WITH_TXN4(db_read_repos_info(revision, repos_relpath,
10246                                           (repos_root_url || repos_uuid)
10247                                             ? &repos_id : NULL,
10248                                           wcroot, local_relpath,
10249                                           result_pool, scratch_pool),
10250                        svn_wc__db_fetch_repos_info(repos_root_url,
10251                                                    repos_uuid,
10252                                                    wcroot, repos_id,
10253                                                    result_pool),
10254                        SVN_NO_ERROR, SVN_NO_ERROR,
10255                        wcroot);
10256
10257   return SVN_NO_ERROR;
10258 }
10259
10260
10261 /* Call RECEIVER_FUNC, passing RECEIVER_BATON, an absolute path, and
10262    a hash table mapping <tt>char *</tt> names onto svn_string_t *
10263    values for any properties of immediate or recursive child nodes of
10264    LOCAL_ABSPATH, the actual query being determined by STMT_IDX.
10265    If FILES_ONLY is true, only report properties for file child nodes.
10266    Check for cancellation between calls of RECEIVER_FUNC.
10267 */
10268 typedef struct cache_props_baton_t
10269 {
10270   svn_depth_t depth;
10271   svn_boolean_t pristine;
10272   const apr_array_header_t *changelists;
10273   svn_cancel_func_t cancel_func;
10274   void *cancel_baton;
10275 } cache_props_baton_t;
10276
10277
10278 static svn_error_t *
10279 cache_props_recursive(void *cb_baton,
10280                       svn_wc__db_wcroot_t *wcroot,
10281                       const char *local_relpath,
10282                       apr_pool_t *scratch_pool)
10283 {
10284   cache_props_baton_t *baton = cb_baton;
10285   svn_sqlite__stmt_t *stmt;
10286   int stmt_idx;
10287
10288   SVN_ERR(populate_targets_tree(wcroot, local_relpath, baton->depth,
10289                                 baton->changelists, scratch_pool));
10290
10291   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb,
10292                                       STMT_CREATE_TARGET_PROP_CACHE));
10293
10294   if (baton->pristine)
10295     stmt_idx = STMT_CACHE_TARGET_PRISTINE_PROPS;
10296   else
10297     stmt_idx = STMT_CACHE_TARGET_PROPS;
10298
10299   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, stmt_idx));
10300   SVN_ERR(svn_sqlite__bind_int64(stmt, 1, wcroot->wc_id));
10301   SVN_ERR(svn_sqlite__step_done(stmt));
10302
10303   return SVN_NO_ERROR;
10304 }
10305
10306
10307 svn_error_t *
10308 svn_wc__db_read_props_streamily(svn_wc__db_t *db,
10309                                 const char *local_abspath,
10310                                 svn_depth_t depth,
10311                                 svn_boolean_t pristine,
10312                                 const apr_array_header_t *changelists,
10313                                 svn_wc__proplist_receiver_t receiver_func,
10314                                 void *receiver_baton,
10315                                 svn_cancel_func_t cancel_func,
10316                                 void *cancel_baton,
10317                                 apr_pool_t *scratch_pool)
10318 {
10319   svn_wc__db_wcroot_t *wcroot;
10320   const char *local_relpath;
10321   svn_sqlite__stmt_t *stmt;
10322   cache_props_baton_t baton;
10323   svn_boolean_t have_row;
10324   apr_pool_t *iterpool;
10325   svn_error_t *err = NULL;
10326
10327   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10328   SVN_ERR_ASSERT(receiver_func);
10329   SVN_ERR_ASSERT((depth == svn_depth_files) ||
10330                  (depth == svn_depth_immediates) ||
10331                  (depth == svn_depth_infinity));
10332
10333   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10334                                                 db, local_abspath,
10335                                                 scratch_pool, scratch_pool));
10336   VERIFY_USABLE_WCROOT(wcroot);
10337
10338   baton.depth = depth;
10339   baton.pristine = pristine;
10340   baton.changelists = changelists;
10341   baton.cancel_func = cancel_func;
10342   baton.cancel_baton = cancel_baton;
10343
10344   SVN_ERR(with_finalization(wcroot, local_relpath,
10345                             cache_props_recursive, &baton,
10346                             NULL, NULL,
10347                             cancel_func, cancel_baton,
10348                             NULL, NULL,
10349                             STMT_DROP_TARGETS_LIST,
10350                             scratch_pool));
10351
10352   iterpool = svn_pool_create(scratch_pool);
10353
10354   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10355                                     STMT_SELECT_ALL_TARGET_PROP_CACHE));
10356   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10357   while (!err && have_row)
10358     {
10359       apr_hash_t *props;
10360
10361       svn_pool_clear(iterpool);
10362
10363       SVN_ERR(svn_sqlite__column_properties(&props, stmt, 1, iterpool,
10364                                             iterpool));
10365
10366       /* see if someone wants to cancel this operation. */
10367       if (cancel_func)
10368         err = cancel_func(cancel_baton);
10369
10370       if (!err && props && apr_hash_count(props) != 0)
10371         {
10372           const char *child_relpath;
10373           const char *child_abspath;
10374
10375           child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
10376           child_abspath = svn_dirent_join(wcroot->abspath,
10377                                           child_relpath, iterpool);
10378
10379           err = receiver_func(receiver_baton, child_abspath, props, iterpool);
10380         }
10381
10382       err = svn_error_compose_create(err, svn_sqlite__step(&have_row, stmt));
10383     }
10384
10385   err = svn_error_compose_create(err, svn_sqlite__reset(stmt));
10386
10387   svn_pool_destroy(iterpool);
10388
10389   SVN_ERR(svn_error_compose_create(
10390                     err,
10391                     svn_sqlite__exec_statements(wcroot->sdb,
10392                                                 STMT_DROP_TARGET_PROP_CACHE)));
10393   return SVN_NO_ERROR;
10394 }
10395
10396
10397 /* Helper for svn_wc__db_read_props().
10398  */
10399 svn_error_t *
10400 svn_wc__db_read_props_internal(apr_hash_t **props,
10401                                svn_wc__db_wcroot_t *wcroot,
10402                                const char *local_relpath,
10403                                apr_pool_t *result_pool,
10404                                apr_pool_t *scratch_pool)
10405 {
10406   svn_sqlite__stmt_t *stmt;
10407   svn_boolean_t have_row;
10408   svn_error_t *err = NULL;
10409
10410   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10411                                     STMT_SELECT_ACTUAL_PROPS));
10412   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10413   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10414
10415   if (have_row && !svn_sqlite__column_is_null(stmt, 0))
10416     {
10417       err = svn_sqlite__column_properties(props, stmt, 0,
10418                                           result_pool, scratch_pool);
10419     }
10420   else
10421     have_row = FALSE;
10422
10423   SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10424
10425   if (have_row)
10426     return SVN_NO_ERROR;
10427
10428   /* No local changes. Return the pristine props for this node.  */
10429   SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, FALSE,
10430                                  result_pool, scratch_pool));
10431   if (*props == NULL)
10432     {
10433       /* Pristine properties are not defined for this node.
10434          ### we need to determine whether this node is in a state that
10435          ### allows for ACTUAL properties (ie. not deleted). for now,
10436          ### just say all nodes, no matter the state, have at least an
10437          ### empty set of props.  */
10438       *props = apr_hash_make(result_pool);
10439     }
10440
10441   return SVN_NO_ERROR;
10442 }
10443
10444
10445 svn_error_t *
10446 svn_wc__db_read_props(apr_hash_t **props,
10447                       svn_wc__db_t *db,
10448                       const char *local_abspath,
10449                       apr_pool_t *result_pool,
10450                       apr_pool_t *scratch_pool)
10451 {
10452   svn_wc__db_wcroot_t *wcroot;
10453   const char *local_relpath;
10454
10455   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10456
10457   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10458                               local_abspath, scratch_pool, scratch_pool));
10459   VERIFY_USABLE_WCROOT(wcroot);
10460
10461   SVN_WC__DB_WITH_TXN(svn_wc__db_read_props_internal(props, wcroot,
10462                                                      local_relpath,
10463                                                      result_pool,
10464                                                      scratch_pool),
10465                       wcroot);
10466
10467   return SVN_NO_ERROR;
10468 }
10469
10470
10471 static svn_error_t *
10472 db_read_pristine_props(apr_hash_t **props,
10473                        svn_wc__db_wcroot_t *wcroot,
10474                        const char *local_relpath,
10475                        svn_boolean_t deleted_ok,
10476                        apr_pool_t *result_pool,
10477                        apr_pool_t *scratch_pool)
10478 {
10479   svn_sqlite__stmt_t *stmt;
10480   svn_boolean_t have_row;
10481   svn_wc__db_status_t presence;
10482
10483   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_NODE_PROPS));
10484   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10485
10486   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10487
10488   if (!have_row)
10489     {
10490       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
10491                                svn_sqlite__reset(stmt),
10492                                _("The node '%s' was not found."),
10493                                path_for_error_message(wcroot,
10494                                                       local_relpath,
10495                                                       scratch_pool));
10496     }
10497
10498
10499   /* Examine the presence: */
10500   presence = svn_sqlite__column_token(stmt, 1, presence_map);
10501
10502   /* For "base-deleted", it is obvious the pristine props are located
10503      below the current node. Fetch the NODE from the next record. */
10504   if (presence == svn_wc__db_status_base_deleted && deleted_ok)
10505     {
10506       SVN_ERR(svn_sqlite__step(&have_row, stmt));
10507
10508       SVN_ERR_ASSERT(have_row);
10509
10510       presence = svn_sqlite__column_token(stmt, 1, presence_map);
10511     }
10512
10513   /* normal or copied: Fetch properties (during update we want
10514      properties for incomplete as well) */
10515   if (presence == svn_wc__db_status_normal
10516       || presence == svn_wc__db_status_incomplete)
10517     {
10518       svn_error_t *err;
10519
10520       err = svn_sqlite__column_properties(props, stmt, 0, result_pool,
10521                                           scratch_pool);
10522       SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
10523
10524       if (!*props)
10525         *props = apr_hash_make(result_pool);
10526
10527       return SVN_NO_ERROR;
10528     }
10529
10530   return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
10531                            svn_sqlite__reset(stmt),
10532                            _("The node '%s' has a status that"
10533                              " has no properties."),
10534                            path_for_error_message(wcroot,
10535                                                   local_relpath,
10536                                                   scratch_pool));
10537 }
10538
10539
10540 svn_error_t *
10541 svn_wc__db_read_pristine_props(apr_hash_t **props,
10542                                svn_wc__db_t *db,
10543                                const char *local_abspath,
10544                                apr_pool_t *result_pool,
10545                                apr_pool_t *scratch_pool)
10546 {
10547   svn_wc__db_wcroot_t *wcroot;
10548   const char *local_relpath;
10549
10550   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10551
10552   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10553                               local_abspath, scratch_pool, scratch_pool));
10554   VERIFY_USABLE_WCROOT(wcroot);
10555
10556   SVN_ERR(db_read_pristine_props(props, wcroot, local_relpath, TRUE,
10557                                  result_pool, scratch_pool));
10558   return SVN_NO_ERROR;
10559 }
10560
10561 svn_error_t *
10562 svn_wc__db_prop_retrieve_recursive(apr_hash_t **values,
10563                                    svn_wc__db_t *db,
10564                                    const char *local_abspath,
10565                                    const char *propname,
10566                                    apr_pool_t *result_pool,
10567                                    apr_pool_t *scratch_pool)
10568 {
10569   svn_wc__db_wcroot_t *wcroot;
10570   const char *local_relpath;
10571   svn_sqlite__stmt_t *stmt;
10572   svn_boolean_t have_row;
10573   apr_pool_t *iterpool;
10574
10575   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10576
10577   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
10578                               local_abspath, scratch_pool, scratch_pool));
10579   VERIFY_USABLE_WCROOT(wcroot);
10580
10581   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10582                                     STMT_SELECT_CURRENT_PROPS_RECURSIVE));
10583   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10584
10585   *values = apr_hash_make(result_pool);
10586
10587   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10588   iterpool = svn_pool_create(scratch_pool);
10589   while (have_row)
10590   {
10591     apr_hash_t *node_props;
10592     svn_string_t *value;
10593
10594     svn_pool_clear(iterpool);
10595
10596     SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 0,
10597                                           iterpool, iterpool));
10598
10599     value = (node_props
10600                 ? svn_hash_gets(node_props, propname)
10601                 : NULL);
10602
10603     if (value)
10604       {
10605         svn_hash_sets(*values,
10606                       svn_dirent_join(wcroot->abspath,
10607                                       svn_sqlite__column_text(stmt, 1, NULL),
10608                                       result_pool),
10609                       svn_string_dup(value, result_pool));
10610       }
10611
10612     SVN_ERR(svn_sqlite__step(&have_row, stmt));
10613   }
10614
10615   svn_pool_destroy(iterpool);
10616
10617   return svn_error_trace(svn_sqlite__reset(stmt));
10618 }
10619
10620 /* Remove all prop name value pairs from PROP_HASH where the property
10621    name is not PROPNAME. */
10622 static void
10623 filter_unwanted_props(apr_hash_t *prop_hash,
10624                       const char * propname,
10625                       apr_pool_t *scratch_pool)
10626 {
10627   apr_hash_index_t *hi;
10628
10629   for (hi = apr_hash_first(scratch_pool, prop_hash);
10630        hi;
10631        hi = apr_hash_next(hi))
10632     {
10633       const char *ipropname = apr_hash_this_key(hi);
10634
10635       if (strcmp(ipropname, propname) != 0)
10636         svn_hash_sets(prop_hash, ipropname, NULL);
10637     }
10638   return;
10639 }
10640
10641 /* Get the changed properties as stored in the ACTUAL table */
10642 static svn_error_t *
10643 db_get_changed_props(apr_hash_t **actual_props,
10644                      svn_wc__db_wcroot_t *wcroot,
10645                      const char *local_relpath,
10646                      apr_pool_t *result_pool,
10647                      apr_pool_t *scratch_pool)
10648 {
10649   svn_sqlite__stmt_t *stmt;
10650   svn_boolean_t have_row;
10651   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10652                                 STMT_SELECT_ACTUAL_PROPS));
10653   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10654   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10655
10656   if (have_row && !svn_sqlite__column_is_null(stmt, 0))
10657     SVN_ERR(svn_sqlite__column_properties(actual_props, stmt, 0,
10658                                           result_pool, scratch_pool));
10659   else
10660     *actual_props = NULL; /* Cached when we read that record */
10661
10662   return svn_error_trace(svn_sqlite__reset(stmt));
10663 }
10664
10665 /* The body of svn_wc__db_read_inherited_props().  */
10666 static svn_error_t *
10667 db_read_inherited_props(apr_array_header_t **inherited_props,
10668                         apr_hash_t **actual_props,
10669                         svn_wc__db_wcroot_t *wcroot,
10670                         const char *local_relpath,
10671                         const char *propname,
10672                         apr_pool_t *result_pool,
10673                         apr_pool_t *scratch_pool)
10674 {
10675   int i;
10676   apr_array_header_t *cached_iprops = NULL;
10677   apr_array_header_t *iprops;
10678   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
10679   svn_sqlite__stmt_t *stmt;
10680   const char *relpath;
10681   const char *expected_parent_repos_relpath = NULL;
10682   const char *parent_relpath;
10683
10684   iprops = apr_array_make(result_pool, 1,
10685                            sizeof(svn_prop_inherited_item_t *));
10686   *inherited_props = iprops;
10687
10688   if (actual_props)
10689     *actual_props = NULL;
10690
10691   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10692                                     STMT_SELECT_NODE_INFO));
10693
10694   relpath = local_relpath;
10695
10696   /* Walk up to the root of the WC looking for inherited properties.  When we
10697      reach the WC root also check for cached inherited properties. */
10698   for (relpath = local_relpath; relpath; relpath = parent_relpath)
10699     {
10700       svn_boolean_t have_row;
10701       int op_depth;
10702       svn_wc__db_status_t status;
10703       apr_hash_t *node_props;
10704
10705       parent_relpath = relpath[0] ? svn_relpath_dirname(relpath, scratch_pool)
10706                                   : NULL;
10707
10708       svn_pool_clear(iterpool);
10709
10710       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, relpath));
10711
10712       SVN_ERR(svn_sqlite__step(&have_row, stmt));
10713
10714       if (!have_row)
10715         return svn_error_createf(
10716                     SVN_ERR_WC_PATH_NOT_FOUND, svn_sqlite__reset(stmt),
10717                     _("The node '%s' was not found."),
10718                     path_for_error_message(wcroot, relpath,
10719                                            scratch_pool));
10720
10721       op_depth = svn_sqlite__column_int(stmt, 0);
10722
10723       status = svn_sqlite__column_token(stmt, 3, presence_map);
10724
10725       if (status != svn_wc__db_status_normal
10726           && status != svn_wc__db_status_incomplete)
10727         return svn_error_createf(
10728                     SVN_ERR_WC_PATH_UNEXPECTED_STATUS, svn_sqlite__reset(stmt),
10729                     _("The node '%s' has a status that has no properties."),
10730                     path_for_error_message(wcroot, relpath,
10731                                            scratch_pool));
10732
10733       if (op_depth > 0)
10734         {
10735           /* WORKING node. Nothing to check */
10736         }
10737       else if (expected_parent_repos_relpath)
10738         {
10739           const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10740
10741           if (strcmp(expected_parent_repos_relpath, repos_relpath) != 0)
10742             {
10743               /* The child of this node has a different parent than this node
10744                  (It is "switched"), so we can stop here. Note that switched
10745                  with the same parent is not interesting for us here. */
10746               SVN_ERR(svn_sqlite__reset(stmt));
10747               break;
10748             }
10749
10750           expected_parent_repos_relpath =
10751               svn_relpath_dirname(expected_parent_repos_relpath, scratch_pool);
10752         }
10753       else
10754         {
10755           const char *repos_relpath = svn_sqlite__column_text(stmt, 2, NULL);
10756
10757           expected_parent_repos_relpath =
10758               svn_relpath_dirname(repos_relpath, scratch_pool);
10759         }
10760
10761       if (op_depth == 0
10762           && !svn_sqlite__column_is_null(stmt, 16))
10763         {
10764           /* The node contains a cache. No reason to look further */
10765           SVN_ERR(svn_sqlite__column_iprops(&cached_iprops, stmt, 16,
10766                                             result_pool, iterpool));
10767
10768           parent_relpath = NULL; /* Stop after this */
10769         }
10770
10771       SVN_ERR(svn_sqlite__column_properties(&node_props, stmt, 14,
10772                                             iterpool, iterpool));
10773
10774       SVN_ERR(svn_sqlite__reset(stmt));
10775
10776       /* If PARENT_ABSPATH is a parent of LOCAL_ABSPATH, then LOCAL_ABSPATH
10777          can inherit properties from it. */
10778       if (relpath != local_relpath)
10779         {
10780           apr_hash_t *changed_props;
10781
10782           SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10783                                        result_pool, iterpool));
10784
10785           if (changed_props)
10786             node_props = changed_props;
10787           else if (node_props)
10788             node_props = svn_prop_hash_dup(node_props, result_pool);
10789
10790           if (node_props && apr_hash_count(node_props))
10791             {
10792               /* If we only want PROPNAME filter out any other properties. */
10793               if (propname)
10794                 filter_unwanted_props(node_props, propname, iterpool);
10795
10796               if (apr_hash_count(node_props))
10797                 {
10798                   svn_prop_inherited_item_t *iprop_elt =
10799                     apr_pcalloc(result_pool,
10800                                 sizeof(svn_prop_inherited_item_t));
10801                   iprop_elt->path_or_url = svn_dirent_join(wcroot->abspath,
10802                                                            relpath,
10803                                                            result_pool);
10804
10805                   iprop_elt->prop_hash = node_props;
10806                   /* Build the output array in depth-first order. */
10807                   svn_sort__array_insert(iprops, &iprop_elt, 0);
10808                 }
10809             }
10810         }
10811       else if (actual_props)
10812         {
10813           apr_hash_t *changed_props;
10814
10815           SVN_ERR(db_get_changed_props(&changed_props, wcroot, relpath,
10816                                        result_pool, iterpool));
10817
10818           if (changed_props)
10819             *actual_props = changed_props;
10820           else if (node_props)
10821             *actual_props = svn_prop_hash_dup(node_props, result_pool);
10822         }
10823     }
10824
10825   if (cached_iprops)
10826     {
10827       for (i = cached_iprops->nelts - 1; i >= 0; i--)
10828         {
10829           svn_prop_inherited_item_t *cached_iprop =
10830             APR_ARRAY_IDX(cached_iprops, i, svn_prop_inherited_item_t *);
10831
10832           /* An empty property hash in the iprops cache means there are no
10833              inherited properties. */
10834           if (apr_hash_count(cached_iprop->prop_hash) == 0)
10835             continue;
10836
10837           if (propname)
10838             filter_unwanted_props(cached_iprop->prop_hash, propname,
10839                                   scratch_pool);
10840
10841           /* If we didn't filter everything then keep this iprop. */
10842           if (apr_hash_count(cached_iprop->prop_hash))
10843             svn_sort__array_insert(iprops, &cached_iprop, 0);
10844         }
10845     }
10846
10847   if (actual_props && !*actual_props)
10848     *actual_props = apr_hash_make(result_pool);
10849
10850   svn_pool_destroy(iterpool);
10851   return SVN_NO_ERROR;
10852 }
10853
10854 svn_error_t *
10855 svn_wc__db_read_inherited_props(apr_array_header_t **iprops,
10856                                 apr_hash_t **actual_props,
10857                                 svn_wc__db_t *db,
10858                                 const char *local_abspath,
10859                                 const char *propname,
10860                                 apr_pool_t *result_pool,
10861                                 apr_pool_t *scratch_pool)
10862 {
10863   svn_wc__db_wcroot_t *wcroot;
10864   const char *local_relpath;
10865
10866   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
10867
10868   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
10869                                                 db, local_abspath,
10870                                                 scratch_pool, scratch_pool));
10871   VERIFY_USABLE_WCROOT(wcroot);
10872
10873   SVN_WC__DB_WITH_TXN(db_read_inherited_props(iprops, actual_props,
10874                                               wcroot, local_relpath, propname,
10875                                               result_pool, scratch_pool),
10876                       wcroot);
10877
10878   return SVN_NO_ERROR;
10879 }
10880
10881 /* The body of svn_wc__db_get_children_with_cached_iprops().
10882  */
10883 static svn_error_t *
10884 get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10885                                 svn_wc__db_wcroot_t *wcroot,
10886                                 const char *local_relpath,
10887                                 svn_depth_t depth,
10888                                 apr_pool_t *result_pool,
10889                                 apr_pool_t *scratch_pool)
10890 {
10891   svn_sqlite__stmt_t *stmt;
10892   svn_boolean_t have_row;
10893
10894   *iprop_paths = apr_hash_make(result_pool);
10895
10896   /* First check if LOCAL_RELPATH itself has iprops */
10897   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10898                                     STMT_SELECT_IPROPS_NODE));
10899   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10900   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10901
10902   if (have_row)
10903    {
10904       const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10905                                                                NULL);
10906       const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10907                                                        relpath_with_cache,
10908                                                        result_pool);
10909       svn_hash_sets(*iprop_paths, abspath_with_cache,
10910                     svn_sqlite__column_text(stmt, 1, result_pool));
10911     }
10912   SVN_ERR(svn_sqlite__reset(stmt));
10913
10914   if (depth == svn_depth_empty)
10915     return SVN_NO_ERROR;
10916
10917   /* Now fetch information for children or all descendants */
10918   if (depth == svn_depth_files
10919       || depth == svn_depth_immediates)
10920     {
10921       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10922                                         STMT_SELECT_IPROPS_CHILDREN));
10923     }
10924   else /* Default to svn_depth_infinity. */
10925     {
10926       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
10927                                         STMT_SELECT_IPROPS_RECURSIVE));
10928     }
10929
10930   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
10931   SVN_ERR(svn_sqlite__step(&have_row, stmt));
10932
10933   while (have_row)
10934     {
10935       const char *relpath_with_cache = svn_sqlite__column_text(stmt, 0,
10936                                                                NULL);
10937       const char *abspath_with_cache = svn_dirent_join(wcroot->abspath,
10938                                                        relpath_with_cache,
10939                                                        result_pool);
10940       svn_hash_sets(*iprop_paths, abspath_with_cache,
10941                     svn_sqlite__column_text(stmt, 1, result_pool));
10942       SVN_ERR(svn_sqlite__step(&have_row, stmt));
10943     }
10944
10945   SVN_ERR(svn_sqlite__reset(stmt));
10946
10947   /* For depth files we should filter non files */
10948   if (depth == svn_depth_files)
10949     {
10950       apr_hash_index_t *hi;
10951       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
10952
10953       for (hi = apr_hash_first(scratch_pool, *iprop_paths);
10954            hi;
10955            hi = apr_hash_next(hi))
10956         {
10957           const char *child_abspath = apr_hash_this_key(hi);
10958           const char *child_relpath;
10959           svn_node_kind_t child_kind;
10960
10961           svn_pool_clear(iterpool);
10962
10963           child_relpath = svn_dirent_is_child(local_relpath, child_abspath,
10964                                               NULL);
10965
10966           if (! child_relpath)
10967             {
10968               continue; /* local_relpath itself */
10969             }
10970
10971           SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &child_kind, NULL,
10972                                                     NULL, NULL, NULL, NULL,
10973                                                     NULL, NULL, NULL, NULL,
10974                                                     NULL, NULL, NULL, NULL,
10975                                                     wcroot, child_relpath,
10976                                                     scratch_pool,
10977                                                     scratch_pool));
10978
10979           /* Filter if not a file */
10980           if (child_kind != svn_node_file)
10981             {
10982               svn_hash_sets(*iprop_paths, child_abspath, NULL);
10983             }
10984         }
10985
10986       svn_pool_destroy(iterpool);
10987     }
10988
10989   return SVN_NO_ERROR;
10990 }
10991
10992 svn_error_t *
10993 svn_wc__db_get_children_with_cached_iprops(apr_hash_t **iprop_paths,
10994                                            svn_depth_t depth,
10995                                            const char *local_abspath,
10996                                            svn_wc__db_t *db,
10997                                            apr_pool_t *result_pool,
10998                                            apr_pool_t *scratch_pool)
10999 {
11000   svn_wc__db_wcroot_t *wcroot;
11001   const char *local_relpath;
11002
11003   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11004
11005   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11006                                                 local_abspath, scratch_pool,
11007                                                 scratch_pool));
11008   VERIFY_USABLE_WCROOT(wcroot);
11009
11010   SVN_WC__DB_WITH_TXN(
11011     get_children_with_cached_iprops(iprop_paths, wcroot, local_relpath,
11012                                     depth, result_pool, scratch_pool),
11013     wcroot);
11014
11015   return SVN_NO_ERROR;
11016 }
11017
11018 svn_error_t *
11019 svn_wc__db_read_children_of_working_node(const apr_array_header_t **children,
11020                                          svn_wc__db_t *db,
11021                                          const char *local_abspath,
11022                                          apr_pool_t *result_pool,
11023                                          apr_pool_t *scratch_pool)
11024 {
11025   svn_wc__db_wcroot_t *wcroot;
11026   const char *local_relpath;
11027
11028   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11029
11030   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11031                                              local_abspath,
11032                                              scratch_pool, scratch_pool));
11033   VERIFY_USABLE_WCROOT(wcroot);
11034
11035   return svn_error_trace(
11036           gather_children(children, wcroot, local_relpath,
11037                           STMT_SELECT_WORKING_CHILDREN, -1,
11038                           result_pool, scratch_pool));
11039 }
11040
11041 svn_error_t *
11042 svn_wc__db_base_read_not_present_children(
11043                                 const apr_array_header_t **children,
11044                                 svn_wc__db_t *db,
11045                                 const char *local_abspath,
11046                                 apr_pool_t *result_pool,
11047                                 apr_pool_t *scratch_pool)
11048 {
11049   svn_wc__db_wcroot_t *wcroot;
11050   const char *local_relpath;
11051
11052   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11053
11054   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11055                                              local_abspath,
11056                                              scratch_pool, scratch_pool));
11057   VERIFY_USABLE_WCROOT(wcroot);
11058
11059   return svn_error_trace(
11060           gather_children(children, wcroot, local_relpath,
11061                           STMT_SELECT_BASE_NOT_PRESENT_CHILDREN, -1,
11062                           result_pool, scratch_pool));
11063 }
11064
11065 /* Helper for svn_wc__db_node_check_replace().
11066  */
11067 static svn_error_t *
11068 check_replace_txn(svn_boolean_t *is_replace_root_p,
11069                   svn_boolean_t *base_replace_p,
11070                   svn_boolean_t *is_replace_p,
11071                   svn_wc__db_wcroot_t *wcroot,
11072                   const char *local_relpath,
11073                   apr_pool_t *scratch_pool)
11074 {
11075   svn_sqlite__stmt_t *stmt;
11076   svn_boolean_t have_row;
11077   svn_boolean_t is_replace = FALSE;
11078   int replaced_op_depth;
11079   svn_wc__db_status_t replaced_status;
11080
11081   /* Our caller initialized the output values to FALSE */
11082
11083   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11084                                     STMT_SELECT_NODE_INFO));
11085
11086   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11087
11088   SVN_ERR(svn_sqlite__step(&have_row, stmt));
11089
11090   if (!have_row)
11091     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
11092                              svn_sqlite__reset(stmt),
11093                              _("The node '%s' was not found."),
11094                              path_for_error_message(wcroot, local_relpath,
11095                                                     scratch_pool));
11096
11097   {
11098     svn_wc__db_status_t status;
11099
11100     status = svn_sqlite__column_token(stmt, 3, presence_map);
11101
11102     if (status != svn_wc__db_status_normal)
11103       return svn_error_trace(svn_sqlite__reset(stmt));
11104   }
11105
11106   SVN_ERR(svn_sqlite__step(&have_row, stmt));
11107
11108   if (!have_row)
11109     return svn_error_trace(svn_sqlite__reset(stmt));
11110
11111   replaced_status = svn_sqlite__column_token(stmt, 3, presence_map);
11112
11113   /* If the layer below the add describes a not present or a deleted node,
11114      this is not a replacement. Deleted can only occur if an ancestor is
11115      the delete root. */
11116   if (replaced_status != svn_wc__db_status_not_present
11117       && replaced_status != svn_wc__db_status_excluded
11118       && replaced_status != svn_wc__db_status_server_excluded
11119       && replaced_status != svn_wc__db_status_base_deleted)
11120     {
11121       is_replace = TRUE;
11122       if (is_replace_p)
11123         *is_replace_p = TRUE;
11124     }
11125
11126   replaced_op_depth = svn_sqlite__column_int(stmt, 0);
11127
11128   if (base_replace_p)
11129     {
11130       int op_depth = svn_sqlite__column_int(stmt, 0);
11131
11132       while (op_depth != 0 && have_row)
11133         {
11134           SVN_ERR(svn_sqlite__step(&have_row, stmt));
11135
11136           if (have_row)
11137             op_depth = svn_sqlite__column_int(stmt, 0);
11138         }
11139
11140       if (have_row && op_depth == 0)
11141         {
11142           svn_wc__db_status_t base_status;
11143
11144           base_status = svn_sqlite__column_token(stmt, 3, presence_map);
11145
11146           *base_replace_p = (base_status != svn_wc__db_status_not_present);
11147         }
11148     }
11149
11150   SVN_ERR(svn_sqlite__reset(stmt));
11151
11152   if (!is_replace_root_p || !is_replace)
11153     return SVN_NO_ERROR;
11154
11155   if (replaced_status != svn_wc__db_status_base_deleted)
11156     {
11157       int parent_op_depth;
11158
11159       /* Check the current op-depth of the parent to see if we are a replacement
11160          root */
11161       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
11162                                 svn_relpath_dirname(local_relpath,
11163                                                     scratch_pool)));
11164
11165       SVN_ERR(svn_sqlite__step_row(stmt)); /* Parent must exist as 'normal' */
11166
11167       parent_op_depth = svn_sqlite__column_int(stmt, 0);
11168
11169       if (parent_op_depth >= replaced_op_depth)
11170         {
11171           /* Did we replace inside our directory? */
11172
11173           *is_replace_root_p = (parent_op_depth == replaced_op_depth);
11174           SVN_ERR(svn_sqlite__reset(stmt));
11175           return SVN_NO_ERROR;
11176         }
11177
11178       SVN_ERR(svn_sqlite__step(&have_row, stmt));
11179
11180       if (have_row)
11181         parent_op_depth = svn_sqlite__column_int(stmt, 0);
11182
11183       SVN_ERR(svn_sqlite__reset(stmt));
11184
11185       if (!have_row)
11186         *is_replace_root_p = TRUE; /* Parent is no replacement */
11187       else if (parent_op_depth < replaced_op_depth)
11188         *is_replace_root_p = TRUE; /* Parent replaces a lower layer */
11189       /*else // No replacement root */
11190   }
11191
11192   return SVN_NO_ERROR;
11193 }
11194
11195 svn_error_t *
11196 svn_wc__db_node_check_replace(svn_boolean_t *is_replace_root,
11197                               svn_boolean_t *base_replace,
11198                               svn_boolean_t *is_replace,
11199                               svn_wc__db_t *db,
11200                               const char *local_abspath,
11201                               apr_pool_t *scratch_pool)
11202 {
11203   svn_wc__db_wcroot_t *wcroot;
11204   const char *local_relpath;
11205
11206   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11207
11208   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11209                                              local_abspath,
11210                                              scratch_pool, scratch_pool));
11211   VERIFY_USABLE_WCROOT(wcroot);
11212
11213   if (is_replace_root)
11214     *is_replace_root = FALSE;
11215   if (base_replace)
11216     *base_replace = FALSE;
11217   if (is_replace)
11218     *is_replace = FALSE;
11219
11220   if (local_relpath[0] == '\0')
11221     return SVN_NO_ERROR; /* Working copy root can't be replaced */
11222
11223   SVN_WC__DB_WITH_TXN(
11224     check_replace_txn(is_replace_root, base_replace, is_replace,
11225                       wcroot, local_relpath, scratch_pool),
11226     wcroot);
11227
11228   return SVN_NO_ERROR;
11229 }
11230
11231 svn_error_t *
11232 svn_wc__db_read_children(const apr_array_header_t **children,
11233                          svn_wc__db_t *db,
11234                          const char *local_abspath,
11235                          apr_pool_t *result_pool,
11236                          apr_pool_t *scratch_pool)
11237 {
11238   svn_wc__db_wcroot_t *wcroot;
11239   const char *local_relpath;
11240
11241   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11242
11243   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11244                                              local_abspath,
11245                                              scratch_pool, scratch_pool));
11246   VERIFY_USABLE_WCROOT(wcroot);
11247
11248   return gather_children(children, wcroot, local_relpath,
11249                          STMT_SELECT_NODE_CHILDREN, -1,
11250                          result_pool, scratch_pool);
11251 }
11252
11253
11254 /* Implementation of svn_wc__db_global_relocate */
11255 static svn_error_t *
11256 relocate_txn(svn_wc__db_wcroot_t *wcroot,
11257              const char *local_relpath,
11258              const char *repos_root_url,
11259              apr_pool_t *scratch_pool)
11260 {
11261   svn_sqlite__stmt_t *stmt;
11262   apr_int64_t new_repos_id;
11263   const char *local_dir_relpath;
11264   svn_wc__db_status_t status;
11265   const char *repos_uuid;
11266   svn_boolean_t have_base_node;
11267   apr_int64_t old_repos_id;
11268
11269   local_dir_relpath = local_relpath;
11270
11271   SVN_ERR(read_info(&status,
11272                     NULL, NULL, NULL, &old_repos_id,
11273                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11274                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11275                     NULL,
11276                     &have_base_node, NULL, NULL,
11277                     wcroot, local_relpath,
11278                     scratch_pool, scratch_pool));
11279
11280   if (status == svn_wc__db_status_excluded)
11281     {
11282       /* The parent cannot be excluded, so look at the parent and then
11283          adjust the relpath */
11284       const char *parent_relpath = svn_relpath_dirname(local_dir_relpath,
11285                                                        scratch_pool);
11286       SVN_ERR(read_info(&status,
11287                         NULL, NULL, NULL, &old_repos_id,
11288                         NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11289                         NULL, NULL, NULL, NULL, NULL, NULL, NULL,
11290                         NULL, NULL, NULL,
11291                         NULL, NULL, NULL,
11292                         wcroot, parent_relpath,
11293                         scratch_pool, scratch_pool));
11294       local_dir_relpath = parent_relpath;
11295     }
11296
11297   if (old_repos_id == INVALID_REPOS_ID)
11298     {
11299       /* Do we need to support relocating something that is
11300          added/deleted/excluded without relocating the parent?  If not
11301          then perhaps relpath, root_url and uuid should be passed down
11302          to the children so that they don't have to scan? */
11303
11304       if (status == svn_wc__db_status_deleted)
11305         {
11306           const char *work_del_relpath;
11307
11308           SVN_ERR(scan_deletion(NULL, NULL,
11309                                 &work_del_relpath, NULL,
11310                                 wcroot, local_dir_relpath,
11311                                 scratch_pool,
11312                                 scratch_pool));
11313           if (work_del_relpath)
11314             {
11315               /* Deleted within a copy/move */
11316
11317               /* The parent of the delete is added. */
11318               status = svn_wc__db_status_added;
11319               local_dir_relpath = svn_relpath_dirname(work_del_relpath,
11320                                                       scratch_pool);
11321             }
11322         }
11323
11324       if (status == svn_wc__db_status_added)
11325         {
11326           SVN_ERR(scan_addition(NULL, NULL, NULL, &old_repos_id,
11327                                 NULL, NULL, NULL, NULL, NULL, NULL,
11328                                 wcroot, local_dir_relpath,
11329                                 scratch_pool, scratch_pool));
11330         }
11331       else
11332         SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL, NULL,
11333                                                   &old_repos_id,
11334                                                   NULL, NULL, NULL, NULL, NULL,
11335                                                   NULL, NULL, NULL, NULL, NULL,
11336                                                   wcroot, local_dir_relpath,
11337                                                   scratch_pool, scratch_pool));
11338     }
11339
11340   SVN_ERR(svn_wc__db_fetch_repos_info(NULL, &repos_uuid, wcroot,
11341                                       old_repos_id, scratch_pool));
11342   SVN_ERR_ASSERT(repos_uuid);
11343
11344   /* This function affects all the children of the given local_relpath,
11345      but the way that it does this is through the repos inheritance mechanism.
11346      So, we only need to rewrite the repos_id of the given local_relpath,
11347      as well as any children with a non-null repos_id, as well as various
11348      repos_id fields in the locks and working_node tables.
11349    */
11350
11351   /* Get the repos_id for the new repository. */
11352   SVN_ERR(create_repos_id(&new_repos_id, repos_root_url, repos_uuid,
11353                           wcroot->sdb, scratch_pool));
11354
11355   /* Set the (base and working) repos_ids and clear the dav_caches */
11356   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11357                                     STMT_RECURSIVE_UPDATE_NODE_REPO));
11358   SVN_ERR(svn_sqlite__bindf(stmt, "isii", wcroot->wc_id, local_relpath,
11359                             old_repos_id, new_repos_id));
11360   SVN_ERR(svn_sqlite__step_done(stmt));
11361
11362   if (have_base_node)
11363     {
11364       /* Update any locks for the root or its children. */
11365       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11366                                         STMT_UPDATE_LOCK_REPOS_ID));
11367       SVN_ERR(svn_sqlite__bindf(stmt, "ii", old_repos_id, new_repos_id));
11368       SVN_ERR(svn_sqlite__step_done(stmt));
11369     }
11370
11371   /* ### TODO: Update urls stored in inherited properties...
11372                What about urls in conflicts?
11373                  # We can probably keep these as they are only used
11374                    for showing full urls to the user */
11375
11376   return SVN_NO_ERROR;
11377 }
11378
11379
11380 svn_error_t *
11381 svn_wc__db_global_relocate(svn_wc__db_t *db,
11382                            const char *local_dir_abspath,
11383                            const char *repos_root_url,
11384                            apr_pool_t *scratch_pool)
11385 {
11386   svn_wc__db_wcroot_t *wcroot;
11387   const char *local_relpath;
11388
11389   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
11390   /* ### assert that we were passed a directory?  */
11391
11392   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
11393                            db, local_dir_abspath, scratch_pool, scratch_pool));
11394   VERIFY_USABLE_WCROOT(wcroot);
11395
11396   SVN_WC__DB_WITH_TXN(
11397     relocate_txn(wcroot, local_relpath, repos_root_url, scratch_pool),
11398     wcroot);
11399
11400   SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_infinity,
11401                         scratch_pool));
11402
11403   return SVN_NO_ERROR;
11404 }
11405
11406
11407 /* Helper for commit_node()
11408    Set *REPOS_ID and *REPOS_RELPATH to the BASE repository location of
11409    (WCROOT, LOCAL_RELPATH), directly if its BASE row exists or implied from
11410    its parent's BASE row if not. In the latter case, error if the parent
11411    BASE row does not exist.  */
11412 static svn_error_t *
11413 determine_commit_repos_info(apr_int64_t *repos_id,
11414                             const char **repos_relpath,
11415                             svn_wc__db_wcroot_t *wcroot,
11416                             const char *local_relpath,
11417                             apr_pool_t *result_pool,
11418                             apr_pool_t *scratch_pool)
11419 {
11420   svn_sqlite__stmt_t *stmt;
11421   svn_boolean_t have_row;
11422   int op_depth;
11423
11424   /* Prefer the current node's repository information.  */
11425   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11426                                     STMT_SELECT_NODE_INFO));
11427   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11428   SVN_ERR(svn_sqlite__step(&have_row, stmt));
11429
11430   if (!have_row)
11431     return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
11432                              svn_sqlite__reset(stmt),
11433                              _("The node '%s' was not found."),
11434                              path_for_error_message(wcroot, local_relpath,
11435                                                     scratch_pool));
11436
11437   op_depth = svn_sqlite__column_int(stmt, 0);
11438
11439   if (op_depth > 0)
11440     {
11441       svn_wc__db_status_t presence = svn_sqlite__column_token(stmt, 3,
11442                                                               presence_map);
11443
11444       if (presence == svn_wc__db_status_base_deleted)
11445         {
11446           SVN_ERR(svn_sqlite__step_row(stmt)); /* There must be a row */
11447           op_depth = svn_sqlite__column_int(stmt, 0);
11448         }
11449       else
11450         {
11451           const char *parent_repos_relpath;
11452           const char *parent_relpath;
11453           const char *name;
11454
11455           SVN_ERR(svn_sqlite__reset(stmt));
11456
11457           /* The repository relative path of an add/copy is based on its
11458              ancestor, not on the shadowed base layer.
11459
11460              As this function is only used from the commit processing we know
11461              the parent directory has only a BASE row, so we can just obtain
11462              the information directly by recursing (once!)  */
11463
11464           svn_relpath_split(&parent_relpath, &name, local_relpath,
11465                             scratch_pool);
11466
11467           SVN_ERR(determine_commit_repos_info(repos_id, &parent_repos_relpath,
11468                                               wcroot, parent_relpath,
11469                                               scratch_pool, scratch_pool));
11470
11471           *repos_relpath = svn_relpath_join(parent_repos_relpath, name,
11472                                             result_pool);
11473           return SVN_NO_ERROR;
11474         }
11475     }
11476
11477
11478   SVN_ERR_ASSERT(op_depth == 0); /* And that row must be BASE */
11479
11480   SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 1));
11481   SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt, 2));
11482
11483   *repos_id = svn_sqlite__column_int64(stmt, 1);
11484   *repos_relpath = svn_sqlite__column_text(stmt, 2, result_pool);
11485
11486   return svn_error_trace(svn_sqlite__reset(stmt));
11487 }
11488
11489 static svn_error_t *
11490 moved_descendant_collect(apr_hash_t **map,
11491                         svn_wc__db_wcroot_t *wcroot,
11492                         const char *local_relpath,
11493                         int op_depth,
11494                         apr_pool_t *result_pool,
11495                         apr_pool_t *scratch_pool)
11496 {
11497   svn_sqlite__stmt_t *stmt;
11498   svn_boolean_t have_row;
11499
11500   *map = NULL;
11501
11502   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11503                                     STMT_SELECT_MOVED_DESCENDANTS_SRC));
11504   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
11505                                          local_relpath,
11506                                          op_depth));
11507
11508   SVN_ERR(svn_sqlite__step(&have_row, stmt));
11509   if (! have_row)
11510     return svn_error_trace(svn_sqlite__reset(stmt));
11511
11512   /* Find all moved descendants. Key them on target, because that is
11513      always unique */
11514   while (have_row)
11515     {
11516       const char *src_relpath = svn_sqlite__column_text(stmt, 1, result_pool);
11517       const char *to_relpath = svn_sqlite__column_text(stmt, 4, result_pool);
11518
11519       if (!*map)
11520         *map = apr_hash_make(result_pool);
11521
11522       svn_hash_sets(*map, to_relpath, src_relpath);
11523
11524       SVN_ERR(svn_sqlite__step(&have_row, stmt));
11525     }
11526   SVN_ERR(svn_sqlite__reset(stmt));
11527
11528   return SVN_NO_ERROR;
11529 }
11530
11531 /* Helper for svn_wc__db_global_commit()
11532
11533    Makes local_relpath and all its descendants at the same op-depth represent
11534    the copy origin repos_id:repos_relpath@revision.
11535
11536    This code is only valid to fix-up a move from an old location, to a new
11537    location during a commit.
11538
11539    Assumptions:
11540      * local_relpath is not the working copy root (can't be moved)
11541      * repos_relpath is not the repository root (can't be moved)
11542  */
11543 static svn_error_t *
11544 moved_descendant_commit(svn_wc__db_wcroot_t *wcroot,
11545                         const char *local_relpath,
11546                         apr_int64_t repos_id,
11547                         const char *repos_relpath,
11548                         svn_revnum_t revision,
11549                         apr_hash_t *children,
11550                         apr_pool_t *scratch_pool)
11551 {
11552   apr_pool_t *iterpool;
11553   svn_sqlite__stmt_t *stmt;
11554   apr_hash_index_t *hi;
11555
11556   SVN_ERR_ASSERT(*local_relpath != '\0'
11557                  && *repos_relpath != '\0');
11558
11559   if (!children)
11560     return SVN_NO_ERROR;
11561
11562   /* Then update them */
11563   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11564                                     STMT_COMMIT_UPDATE_ORIGIN));
11565
11566   iterpool = svn_pool_create(scratch_pool);
11567   for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
11568     {
11569       const char *src_relpath = apr_hash_this_val(hi);
11570       const char *to_relpath = apr_hash_this_key(hi);
11571       const char *new_repos_relpath;
11572       int to_op_depth = relpath_depth(to_relpath);
11573       int affected;
11574       apr_hash_t *map;
11575
11576       svn_pool_clear(iterpool);
11577
11578       SVN_ERR_ASSERT(to_op_depth > 0);
11579
11580       new_repos_relpath = svn_relpath_join(
11581                             repos_relpath,
11582                             svn_relpath_skip_ancestor(local_relpath,
11583                                                       src_relpath),
11584                             iterpool);
11585
11586       SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11587                                                 to_relpath,
11588                                                 to_op_depth,
11589                                                 repos_id,
11590                                                 new_repos_relpath,
11591                                                 revision));
11592       SVN_ERR(svn_sqlite__update(&affected, stmt));
11593
11594 #ifdef SVN_DEBUG
11595       /* Enable in release code?
11596          Broken moves are not fatal yet, but this assertion would break
11597          committing them */
11598       SVN_ERR_ASSERT(affected >= 1); /* If this fails there is no move dest */
11599 #endif
11600
11601       SVN_ERR(moved_descendant_collect(&map, wcroot, to_relpath, to_op_depth,
11602                                        iterpool, iterpool));
11603       SVN_ERR(moved_descendant_commit(wcroot, to_relpath,
11604                                       repos_id, new_repos_relpath, revision,
11605                                       map, iterpool));
11606     }
11607
11608   svn_pool_destroy(iterpool);
11609   return SVN_NO_ERROR;
11610 }
11611
11612 /* Helper for svn_wc__db_global_commit()
11613
11614    Moves all nodes below LOCAL_RELPATH from op-depth OP_DEPTH to op-depth 0
11615    (BASE), setting their presence to 'not-present' if their presence wasn't
11616    'normal'.
11617
11618    Makes all nodes below LOCAL_RELPATH represent the descendants of repository
11619    location repos_id:repos_relpath@revision.
11620
11621    Assumptions:
11622      * local_relpath is not the working copy root (can't be replaced)
11623      * repos_relpath is not the repository root (can't be replaced)
11624    */
11625 static svn_error_t *
11626 descendant_commit(svn_wc__db_wcroot_t *wcroot,
11627                   const char *local_relpath,
11628                   int op_depth,
11629                   apr_int64_t repos_id,
11630                   const char *repos_relpath,
11631                   svn_revnum_t revision,
11632                   apr_pool_t *scratch_pool)
11633 {
11634   svn_sqlite__stmt_t *stmt;
11635
11636   SVN_ERR_ASSERT(*local_relpath != '\0'
11637                  && *repos_relpath != '\0');
11638
11639   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11640                                     STMT_COMMIT_DESCENDANTS_TO_BASE));
11641
11642   SVN_ERR(svn_sqlite__bindf(stmt, "isdisr", wcroot->wc_id,
11643                                             local_relpath,
11644                                             op_depth,
11645                                             repos_id,
11646                                             repos_relpath,
11647                                             revision));
11648
11649   SVN_ERR(svn_sqlite__update(NULL, stmt));
11650
11651   return SVN_NO_ERROR;
11652 }
11653
11654 /* The body of svn_wc__db_global_commit().
11655  */
11656 static svn_error_t *
11657 commit_node(svn_wc__db_wcroot_t *wcroot,
11658             const char *local_relpath,
11659             svn_revnum_t new_revision,
11660             svn_revnum_t changed_rev,
11661             apr_time_t changed_date,
11662             const char *changed_author,
11663             const svn_checksum_t *new_checksum,
11664             apr_hash_t *new_dav_cache,
11665             svn_boolean_t keep_changelist,
11666             svn_boolean_t no_unlock,
11667             const svn_skel_t *work_items,
11668             apr_pool_t *scratch_pool)
11669 {
11670   svn_sqlite__stmt_t *stmt_info;
11671   svn_sqlite__stmt_t *stmt_act;
11672   svn_boolean_t have_act;
11673   svn_string_t prop_blob = { 0 };
11674   svn_string_t inherited_prop_blob = { 0 };
11675   const char *changelist = NULL;
11676   const char *parent_relpath;
11677   svn_wc__db_status_t new_presence;
11678   svn_node_kind_t new_kind;
11679   const char *new_depth_str = NULL;
11680   svn_sqlite__stmt_t *stmt;
11681   apr_int64_t repos_id;
11682   const char *repos_relpath;
11683   int op_depth;
11684   svn_wc__db_status_t old_presence;
11685   svn_boolean_t moved_here;
11686
11687     /* If we are adding a file or directory, then we need to get
11688      repository information from the parent node since "this node" does
11689      not have a BASE).
11690
11691      For existing nodes, we should retain the (potentially-switched)
11692      repository information.  */
11693   SVN_ERR(determine_commit_repos_info(&repos_id, &repos_relpath,
11694                                       wcroot, local_relpath,
11695                                       scratch_pool, scratch_pool));
11696
11697   /* ### is it better to select only the data needed?  */
11698   SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
11699                                     STMT_SELECT_NODE_INFO));
11700   SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
11701   SVN_ERR(svn_sqlite__step_row(stmt_info));
11702
11703   SVN_ERR(svn_sqlite__get_statement(&stmt_act, wcroot->sdb,
11704                                     STMT_SELECT_ACTUAL_NODE));
11705   SVN_ERR(svn_sqlite__bindf(stmt_act, "is",
11706                             wcroot->wc_id, local_relpath));
11707   SVN_ERR(svn_sqlite__step(&have_act, stmt_act));
11708
11709   /* There should be something to commit!  */
11710
11711   op_depth = svn_sqlite__column_int(stmt_info, 0);
11712
11713   /* Figure out the new node's kind. It will be whatever is in WORKING_NODE,
11714      or there will be a BASE_NODE that has it.  */
11715   old_presence = svn_sqlite__column_token(stmt_info, 3, presence_map);
11716   new_kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
11717
11718   /* What will the new depth be?  */
11719   if (new_kind == svn_node_dir)
11720     new_depth_str = svn_sqlite__column_text(stmt_info, 11, scratch_pool);
11721
11722   /* Check that the repository information is not being changed.  */
11723   if (op_depth == 0)
11724     {
11725       SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 1));
11726       SVN_ERR_ASSERT(!svn_sqlite__column_is_null(stmt_info, 2));
11727
11728       /* A commit cannot change these values.  */
11729       SVN_ERR_ASSERT(repos_id == svn_sqlite__column_int64(stmt_info, 1));
11730       SVN_ERR_ASSERT(strcmp(repos_relpath,
11731                             svn_sqlite__column_text(stmt_info, 2, NULL)) == 0);
11732     }
11733
11734   if (old_presence != svn_wc__db_status_base_deleted)
11735     {
11736       /* Find the appropriate new properties -- ACTUAL overrides any properties
11737          in WORKING that arrived as part of a copy/move.
11738
11739          Note: we'll keep them as a big blob of data, rather than
11740          deserialize/serialize them.  */
11741       if (have_act)
11742         prop_blob.data = svn_sqlite__column_blob(stmt_act, 1, &prop_blob.len,
11743                                                  scratch_pool);
11744       if (prop_blob.data == NULL)
11745         prop_blob.data = svn_sqlite__column_blob(stmt_info, 14, &prop_blob.len,
11746                                                  scratch_pool);
11747
11748       inherited_prop_blob.data = svn_sqlite__column_blob(
11749                                             stmt_info, 16,
11750                                             &inherited_prop_blob.len,
11751                                             scratch_pool);
11752
11753       if (keep_changelist && have_act)
11754         changelist = svn_sqlite__column_text(stmt_act, 0, scratch_pool);
11755
11756       moved_here = svn_sqlite__column_int(stmt_info, 15);
11757     }
11758   else
11759     {
11760       moved_here = FALSE;
11761       changelist = NULL;
11762     }
11763
11764   /* ### other stuff?  */
11765
11766   SVN_ERR(svn_sqlite__reset(stmt_info));
11767   SVN_ERR(svn_sqlite__reset(stmt_act));
11768
11769   if (op_depth > 0)
11770     {
11771       int affected_rows;
11772
11773       SVN_ERR_ASSERT(op_depth == relpath_depth(local_relpath));
11774
11775       /* First clear the moves that we are going to delete in a bit */
11776       {
11777         apr_hash_t *old_moves;
11778         apr_hash_index_t *hi;
11779         SVN_ERR(moved_descendant_collect(&old_moves, wcroot, local_relpath, 0,
11780                                          scratch_pool, scratch_pool));
11781
11782         if (old_moves)
11783           for (hi = apr_hash_first(scratch_pool, old_moves);
11784                 hi; hi = apr_hash_next(hi))
11785             {
11786               SVN_ERR(clear_moved_here(wcroot, apr_hash_this_key(hi),
11787                                         scratch_pool));
11788             }
11789       }
11790
11791       /* This removes all layers of this node and at the same time determines
11792          if we need to remove shadowed layers below our descendants. */
11793
11794       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11795                                         STMT_DELETE_NODE_ALL_LAYERS));
11796       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11797       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
11798
11799       if (affected_rows > 1)
11800         {
11801           /* We commit a shadowing operation
11802
11803            1) Remove all shadowed nodes
11804            2) And remove all nodes that have a base-deleted as lowest layer,
11805               because 1) removed that layer */
11806
11807           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11808                                             STMT_DELETE_SHADOWED_RECURSIVE));
11809
11810           SVN_ERR(svn_sqlite__bindf(stmt,
11811                                     "isd",
11812                                     wcroot->wc_id,
11813                                     local_relpath,
11814                                     op_depth));
11815
11816           SVN_ERR(svn_sqlite__step_done(stmt));
11817         }
11818
11819       /* Note that while these two calls look so similar that they might
11820          be integrated, they really affect a different op-depth and
11821          completely different nodes (via a different recursion pattern). */
11822
11823       if (old_presence != svn_wc__db_status_base_deleted)
11824         {
11825           /* Collapse descendants of the current op_depth to layer 0,
11826              this includes moved-from/to clearing */
11827           SVN_ERR(descendant_commit(wcroot, local_relpath, op_depth,
11828                                     repos_id, repos_relpath, new_revision,
11829                                     scratch_pool));
11830         }
11831
11832       if (old_presence != svn_wc__db_status_base_deleted)
11833         {
11834           apr_hash_t *moves = NULL;
11835
11836           SVN_ERR(moved_descendant_collect(&moves, wcroot, local_relpath, 0,
11837                                            scratch_pool, scratch_pool));
11838
11839           /* And make the recorded local moves represent moves of the node we
11840              just committed. */
11841           SVN_ERR(moved_descendant_commit(wcroot, local_relpath,
11842                                       repos_id, repos_relpath, new_revision,
11843                                       moves, scratch_pool));
11844         }
11845
11846       if (moved_here)
11847         {
11848           /* This node is no longer modified, so no node was moved here */
11849           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11850                                             STMT_CLEAR_MOVED_TO_FROM_DEST));
11851           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
11852                                                 local_relpath));
11853
11854           SVN_ERR(svn_sqlite__step_done(stmt));
11855         }
11856     }
11857   /* Update or add the BASE_NODE row with all the new information.  */
11858
11859   if (*local_relpath == '\0')
11860     parent_relpath = NULL;
11861   else
11862     parent_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
11863
11864   /* Preserve any incomplete status */
11865   if (old_presence != svn_wc__db_status_base_deleted)
11866     {
11867       new_presence = (old_presence == svn_wc__db_status_incomplete
11868                       ? svn_wc__db_status_incomplete
11869                       : svn_wc__db_status_normal);
11870
11871       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11872                                         STMT_APPLY_CHANGES_TO_BASE_NODE));
11873       /* symlink_target not yet used */
11874       SVN_ERR(svn_sqlite__bindf(stmt, "issisrtstrisnbn",
11875                                 wcroot->wc_id, local_relpath,
11876                                 parent_relpath,
11877                                 repos_id,
11878                                 repos_relpath,
11879                                 new_revision,
11880                                 presence_map, new_presence,
11881                                 new_depth_str,
11882                                 kind_map, new_kind,
11883                                 changed_rev,
11884                                 changed_date,
11885                                 changed_author,
11886                                 prop_blob.data, prop_blob.len));
11887
11888       SVN_ERR(svn_sqlite__bind_checksum(stmt, 13, new_checksum,
11889                                         scratch_pool));
11890       SVN_ERR(svn_sqlite__bind_properties(stmt, 15, new_dav_cache,
11891                                           scratch_pool));
11892       if (inherited_prop_blob.data != NULL)
11893         {
11894           SVN_ERR(svn_sqlite__bind_blob(stmt, 17, inherited_prop_blob.data,
11895                                         inherited_prop_blob.len));
11896         }
11897
11898       SVN_ERR(svn_sqlite__step_done(stmt));
11899     }
11900   else
11901     {
11902       struct insert_base_baton_t ibb;
11903       blank_ibb(&ibb);
11904
11905       ibb.repos_id = repos_id;
11906       ibb.status = svn_wc__db_status_not_present;
11907       ibb.kind = new_kind;
11908       ibb.repos_relpath = repos_relpath;
11909       ibb.revision = new_revision;
11910
11911       SVN_ERR(insert_base_node(&ibb, wcroot, local_relpath, scratch_pool));
11912
11913       keep_changelist = FALSE; /* Nothing there */
11914     }
11915
11916   if (have_act)
11917     {
11918       if (keep_changelist && changelist != NULL)
11919         {
11920           /* The user told us to keep the changelist. Replace the row in
11921              ACTUAL_NODE with the basic keys and the changelist.  */
11922           SVN_ERR(svn_sqlite__get_statement(
11923                     &stmt, wcroot->sdb,
11924                     STMT_RESET_ACTUAL_WITH_CHANGELIST));
11925           SVN_ERR(svn_sqlite__bindf(stmt, "isss",
11926                                     wcroot->wc_id, local_relpath,
11927                                     svn_relpath_dirname(local_relpath,
11928                                                         scratch_pool),
11929                                     changelist));
11930           SVN_ERR(svn_sqlite__step_done(stmt));
11931         }
11932       else
11933         {
11934           /* Toss the ACTUAL_NODE row.  */
11935           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
11936                                             STMT_DELETE_ACTUAL_NODE));
11937           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
11938           SVN_ERR(svn_sqlite__step_done(stmt));
11939         }
11940     }
11941
11942   if (!no_unlock)
11943     {
11944       svn_sqlite__stmt_t *lock_stmt;
11945       svn_boolean_t op_root = (op_depth > 0
11946                                && (relpath_depth(local_relpath) == op_depth));
11947
11948       /* If we are committing an add of a delete, we can assume we own
11949          all locks at or below REPOS_RELPATH (or the server would have
11950          denied the commit). As we must have passed these to the server
11951          we can now safely remove them.
11952        */
11953       SVN_ERR(svn_sqlite__get_statement(&lock_stmt, wcroot->sdb,
11954                                         op_root
11955                                           ? STMT_DELETE_LOCK_RECURSIVELY
11956                                           : STMT_DELETE_LOCK));
11957       SVN_ERR(svn_sqlite__bindf(lock_stmt, "is", repos_id, repos_relpath));
11958       SVN_ERR(svn_sqlite__step_done(lock_stmt));
11959     }
11960
11961   /* Install any work items into the queue, as part of this transaction.  */
11962   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
11963
11964   return SVN_NO_ERROR;
11965 }
11966
11967
11968 svn_error_t *
11969 svn_wc__db_global_commit(svn_wc__db_t *db,
11970                          const char *local_abspath,
11971                          svn_revnum_t new_revision,
11972                          svn_revnum_t changed_revision,
11973                          apr_time_t changed_date,
11974                          const char *changed_author,
11975                          const svn_checksum_t *new_checksum,
11976                          apr_hash_t *new_dav_cache,
11977                          svn_boolean_t keep_changelist,
11978                          svn_boolean_t no_unlock,
11979                          const svn_skel_t *work_items,
11980                          apr_pool_t *scratch_pool)
11981 {
11982   const char *local_relpath;
11983   svn_wc__db_wcroot_t *wcroot;
11984
11985   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
11986   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
11987
11988   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
11989                               local_abspath, scratch_pool, scratch_pool));
11990   VERIFY_USABLE_WCROOT(wcroot);
11991
11992   SVN_WC__DB_WITH_TXN(
11993     commit_node(wcroot, local_relpath,
11994                 new_revision, changed_revision, changed_date, changed_author,
11995                 new_checksum, new_dav_cache, keep_changelist,
11996                 no_unlock, work_items, scratch_pool),
11997     wcroot);
11998
11999   /* We *totally* monkeyed the entries. Toss 'em.  */
12000   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
12001
12002   return SVN_NO_ERROR;
12003 }
12004
12005
12006 svn_error_t *
12007 svn_wc__db_global_update(svn_wc__db_t *db,
12008                          const char *local_abspath,
12009                          svn_node_kind_t new_kind,
12010                          const char *new_repos_relpath,
12011                          svn_revnum_t new_revision,
12012                          const apr_hash_t *new_props,
12013                          svn_revnum_t new_changed_rev,
12014                          apr_time_t new_changed_date,
12015                          const char *new_changed_author,
12016                          const apr_array_header_t *new_children,
12017                          const svn_checksum_t *new_checksum,
12018                          const char *new_target,
12019                          const apr_hash_t *new_dav_cache,
12020                          const svn_skel_t *conflict,
12021                          const svn_skel_t *work_items,
12022                          apr_pool_t *scratch_pool)
12023 {
12024   NOT_IMPLEMENTED();
12025
12026 #if 0
12027   svn_wc__db_wcroot_t *wcroot;
12028   const char *local_relpath;
12029
12030   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12031   /* ### allow NULL for NEW_REPOS_RELPATH to indicate "no change"?  */
12032   SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
12033   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_revision));
12034   SVN_ERR_ASSERT(new_props != NULL);
12035   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_changed_rev));
12036   SVN_ERR_ASSERT((new_children != NULL
12037                   && new_checksum == NULL
12038                   && new_target == NULL)
12039                  || (new_children == NULL
12040                      && new_checksum != NULL
12041                      && new_target == NULL)
12042                  || (new_children == NULL
12043                      && new_checksum == NULL
12044                      && new_target != NULL));
12045
12046   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12047                               local_abspath, scratch_pool, scratch_pool));
12048   VERIFY_USABLE_WCROOT(wcroot);
12049
12050   SVN_WC__DB_WITH_TXN(
12051     update_node(wcroot, local_relpath,
12052                 new_repos_relpath, new_revision, new_props,
12053                 new_changed_rev, new_changed_date, new_changed_author,
12054                 new_children, new_checksum, new_target,
12055                 conflict, work_items, scratch_pool),
12056     wcroot);
12057
12058   /* We *totally* monkeyed the entries. Toss 'em.  */
12059   SVN_ERR(flush_entries(wcroot, local_abspath, scratch_pool));
12060
12061   return SVN_NO_ERROR;
12062 #endif
12063 }
12064
12065 /* Sets a base nodes revision, repository relative path, and/or inherited
12066    propertis. If LOCAL_ABSPATH's rev (REV) is valid, set its revision.  If
12067    SET_REPOS_RELPATH is TRUE set its repository relative path to REPOS_RELPATH
12068    (and make sure its REPOS_ID is still valid).  If IPROPS is not NULL set its
12069    inherited properties to IPROPS, if IPROPS is NULL then clear any the iprops
12070    cache for the base node.
12071  */
12072 static svn_error_t *
12073 db_op_set_rev_repos_relpath_iprops(svn_wc__db_wcroot_t *wcroot,
12074                                    const char *local_relpath,
12075                                    apr_array_header_t *iprops,
12076                                    svn_revnum_t rev,
12077                                    svn_boolean_t set_repos_relpath,
12078                                    const char *repos_relpath,
12079                                    apr_int64_t repos_id,
12080                                    apr_pool_t *scratch_pool)
12081 {
12082   svn_sqlite__stmt_t *stmt;
12083
12084   SVN_ERR(flush_entries(wcroot,
12085                         svn_dirent_join(wcroot->abspath, local_relpath,
12086                                         scratch_pool),
12087                         svn_depth_empty, scratch_pool));
12088
12089
12090   if (SVN_IS_VALID_REVNUM(rev))
12091     {
12092       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12093                                         STMT_UPDATE_BASE_REVISION));
12094
12095       SVN_ERR(svn_sqlite__bindf(stmt, "isr", wcroot->wc_id, local_relpath,
12096                                 rev));
12097
12098       SVN_ERR(svn_sqlite__step_done(stmt));
12099     }
12100
12101   if (set_repos_relpath)
12102     {
12103       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12104                                         STMT_UPDATE_BASE_REPOS));
12105
12106       SVN_ERR(svn_sqlite__bindf(stmt, "isis", wcroot->wc_id, local_relpath,
12107                                 repos_id, repos_relpath));
12108
12109       SVN_ERR(svn_sqlite__step_done(stmt));
12110     }
12111
12112   /* Set or clear iprops. */
12113   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12114                                     STMT_UPDATE_IPROP));
12115   SVN_ERR(svn_sqlite__bindf(stmt, "is",
12116                             wcroot->wc_id,
12117                             local_relpath));
12118   SVN_ERR(svn_sqlite__bind_iprops(stmt, 3, iprops, scratch_pool));
12119   SVN_ERR(svn_sqlite__step_done(stmt));
12120
12121   return SVN_NO_ERROR;
12122 }
12123
12124 /* The main body of bump_revisions_post_update().
12125  *
12126  * Tweak the information for LOCAL_RELPATH in WCROOT.  If NEW_REPOS_RELPATH is
12127  * non-NULL update the entry to the new url specified by NEW_REPOS_RELPATH,
12128  * NEW_REPOS_ID.  If NEW_REV is valid, make this the node's working revision.
12129  *
12130  * NODE_STATUS, NODE_KIND, NODE_REVISION and NODE_REPOS_RELPATH represent the
12131  * values as stored currently in WCROOT for LOCAL_RELPATH.
12132  *
12133  * If WCROOT_IPROPS is not NULL it is a hash mapping const char * absolute
12134  * working copy paths to depth-first ordered arrays of
12135  * svn_prop_inherited_item_t * structures.  If the absolute path equivalent
12136  * of LOCAL_RELPATH exists in WCROOT_IPROPS, then set the hashed value as the
12137  * node's inherited properties.
12138  *
12139  * Unless S_ROOT is TRUE the tweaks might cause the node for LOCAL_ABSPATH to
12140  * be removed from the WC; if IS_ROOT is TRUE this will not happen.
12141  */
12142 static svn_error_t *
12143 bump_node_revision(svn_wc__db_wcroot_t *wcroot,
12144                    const char *local_relpath,
12145                    svn_wc__db_status_t node_status,
12146                    svn_node_kind_t node_kind,
12147                    svn_revnum_t node_revision,
12148                    const char *node_repos_relpath,
12149                    apr_int64_t new_repos_id,
12150                    const char *new_repos_relpath,
12151                    svn_revnum_t new_rev,
12152                    svn_depth_t depth,
12153                    apr_hash_t *exclude_relpaths,
12154                    apr_hash_t *wcroot_iprops,
12155                    svn_boolean_t is_root,
12156                    svn_boolean_t skip_when_dir,
12157                    svn_wc__db_t *db,
12158                    apr_pool_t *scratch_pool)
12159 {
12160   apr_pool_t *iterpool;
12161   apr_hash_t *children;
12162   apr_hash_index_t *hi;
12163   svn_boolean_t set_repos_relpath = FALSE;
12164   svn_depth_t depth_below_here = depth;
12165   apr_array_header_t *iprops = NULL;
12166
12167   if (new_repos_relpath != NULL
12168       && strcmp(node_repos_relpath, new_repos_relpath))
12169     set_repos_relpath = TRUE;
12170
12171   if (wcroot_iprops)
12172     iprops = svn_hash_gets(wcroot_iprops,
12173                            svn_dirent_join(wcroot->abspath, local_relpath,
12174                                            scratch_pool));
12175
12176   if (iprops
12177       || set_repos_relpath
12178       || (SVN_IS_VALID_REVNUM(new_rev) && new_rev != node_revision))
12179     {
12180       SVN_ERR(db_op_set_rev_repos_relpath_iprops(wcroot, local_relpath,
12181                                                  iprops, new_rev,
12182                                                  set_repos_relpath,
12183                                                  new_repos_relpath,
12184                                                  new_repos_id,
12185                                                  scratch_pool));
12186     }
12187
12188   /* Early out */
12189   if (depth <= svn_depth_empty
12190       || node_kind != svn_node_dir
12191       || node_status == svn_wc__db_status_server_excluded
12192       || node_status == svn_wc__db_status_excluded
12193       || node_status == svn_wc__db_status_not_present)
12194     return SVN_NO_ERROR;
12195
12196   /* And now recurse over the children */
12197
12198   depth_below_here = depth;
12199
12200   if (depth == svn_depth_immediates || depth == svn_depth_files)
12201     depth_below_here = svn_depth_empty;
12202
12203   iterpool = svn_pool_create(scratch_pool);
12204
12205   SVN_ERR(base_get_children_info(&children, wcroot, local_relpath, 0,
12206                                  scratch_pool, iterpool));
12207   for (hi = apr_hash_first(scratch_pool, children); hi; hi = apr_hash_next(hi))
12208     {
12209       const char *child_basename = apr_hash_this_key(hi);
12210       const struct svn_wc__db_base_info_t *child_info;
12211       const char *child_local_relpath;
12212       const char *child_repos_relpath = NULL;
12213
12214       svn_pool_clear(iterpool);
12215
12216       child_info = apr_hash_this_val(hi);
12217
12218       if (child_info->update_root && child_info->kind == svn_node_file)
12219         continue; /* Skip file externals */
12220
12221       if (depth < svn_depth_immediates && child_info->kind == svn_node_dir)
12222           continue; /* Skip directories */
12223
12224       child_local_relpath = svn_relpath_join(local_relpath, child_basename,
12225                                              iterpool);
12226
12227       /* Don't touch nodes that can't be touched via the exclude list */
12228       if (svn_hash_gets(exclude_relpaths, child_local_relpath))
12229           continue;
12230
12231       /* If the node is still marked 'not-present', then the server did not
12232           re-add it.  So it's really gone in this revision, thus we remove the
12233           node.
12234
12235           If the node is still marked 'server-excluded' and yet is not the same
12236           revision as new_rev, then the server did not re-add it, nor
12237           re-server-exclude it, so we can remove the node. */
12238       if (child_info->status == svn_wc__db_status_not_present
12239           || (child_info->status == svn_wc__db_status_server_excluded &&
12240               child_info->revnum != new_rev))
12241         {
12242           svn_sqlite__stmt_t *stmt;
12243           SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12244                                     STMT_DELETE_BASE_NODE));
12245           SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, child_local_relpath));
12246           SVN_ERR(svn_sqlite__step_done(stmt));
12247           continue;
12248         }
12249
12250       /* Derive the new URL for the current (child) entry */
12251       if (new_repos_relpath)
12252         child_repos_relpath = svn_relpath_join(new_repos_relpath,
12253                                                child_basename, iterpool);
12254
12255       SVN_ERR(bump_node_revision(wcroot, child_local_relpath,
12256                                  child_info->status,
12257                                  child_info->kind,
12258                                  child_info->revnum,
12259                                  child_info->repos_relpath,
12260                                  new_repos_id,
12261                                  child_repos_relpath, new_rev,
12262                                  depth_below_here,
12263                                  exclude_relpaths, wcroot_iprops,
12264                                  FALSE /* is_root */,
12265                                  (depth < svn_depth_immediates), db,
12266                                  iterpool));
12267     }
12268
12269   /* Cleanup */
12270   svn_pool_destroy(iterpool);
12271
12272   return SVN_NO_ERROR;
12273 }
12274
12275 /* Helper for svn_wc__db_op_bump_revisions_post_update().
12276  */
12277 static svn_error_t *
12278 bump_revisions_post_update(svn_wc__db_wcroot_t *wcroot,
12279                            const char *local_relpath,
12280                            svn_wc__db_t *db,
12281                            svn_depth_t depth,
12282                            const char *new_repos_relpath,
12283                            const char *new_repos_root_url,
12284                            const char *new_repos_uuid,
12285                            svn_revnum_t new_revision,
12286                            apr_hash_t *exclude_relpaths,
12287                            apr_hash_t *wcroot_iprops,
12288                            svn_boolean_t empty_update,
12289                            svn_wc_notify_func2_t notify_func,
12290                            void *notify_baton,
12291                            apr_pool_t *scratch_pool)
12292 {
12293   svn_wc__db_status_t status;
12294   svn_node_kind_t kind;
12295   svn_error_t *err;
12296   apr_int64_t new_repos_id = INVALID_REPOS_ID;
12297   svn_revnum_t revision;
12298   const char *repos_relpath;
12299
12300   err = svn_wc__db_base_get_info_internal(&status, &kind, &revision,
12301                                           &repos_relpath, NULL,
12302                                           NULL, NULL, NULL, NULL, NULL, NULL,
12303                                           NULL, NULL, NULL, NULL,
12304                                           wcroot, local_relpath,
12305                                           scratch_pool, scratch_pool);
12306   if (err && err->apr_err == SVN_ERR_WC_PATH_NOT_FOUND)
12307     {
12308       svn_error_clear(err);
12309       return SVN_NO_ERROR;
12310     }
12311   else
12312     SVN_ERR(err);
12313
12314   switch (status)
12315     {
12316       case svn_wc__db_status_excluded:
12317       case svn_wc__db_status_server_excluded:
12318       case svn_wc__db_status_not_present:
12319         return SVN_NO_ERROR;
12320
12321       /* Explicitly ignore other statii */
12322       default:
12323         break;
12324     }
12325
12326   if (new_repos_root_url != NULL)
12327     SVN_ERR(create_repos_id(&new_repos_id, new_repos_root_url,
12328                             new_repos_uuid,
12329                             wcroot->sdb, scratch_pool));
12330
12331   SVN_ERR(bump_node_revision(wcroot, local_relpath,
12332                              status, kind,  revision, repos_relpath,
12333                              new_repos_id,
12334                              new_repos_relpath, new_revision,
12335                              depth, exclude_relpaths,
12336                              wcroot_iprops,
12337                              TRUE /* is_root */, FALSE, db,
12338                              scratch_pool));
12339
12340   /* ### TODO: Use empty_update flag for change knowledge */
12341   SVN_ERR(svn_wc__db_bump_moved_away(wcroot, local_relpath, depth, db,
12342                                      scratch_pool));
12343
12344   SVN_ERR(svn_wc__db_update_move_list_notify(wcroot, SVN_INVALID_REVNUM,
12345                                              SVN_INVALID_REVNUM, notify_func,
12346                                              notify_baton, scratch_pool));
12347
12348   return SVN_NO_ERROR;
12349 }
12350
12351 svn_error_t *
12352 svn_wc__db_op_bump_revisions_post_update(svn_wc__db_t *db,
12353                                          const char *local_abspath,
12354                                          svn_depth_t depth,
12355                                          const char *new_repos_relpath,
12356                                          const char *new_repos_root_url,
12357                                          const char *new_repos_uuid,
12358                                          svn_revnum_t new_revision,
12359                                          apr_hash_t *exclude_relpaths,
12360                                          apr_hash_t *wcroot_iprops,
12361                                          svn_boolean_t empty_update,
12362                                          svn_wc_notify_func2_t notify_func,
12363                                          void *notify_baton,
12364                                          apr_pool_t *scratch_pool)
12365 {
12366   const char *local_relpath;
12367   svn_wc__db_wcroot_t *wcroot;
12368
12369   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12370                               local_abspath, scratch_pool, scratch_pool));
12371
12372   VERIFY_USABLE_WCROOT(wcroot);
12373
12374   if (svn_hash_gets(exclude_relpaths, local_relpath))
12375     return SVN_NO_ERROR;
12376
12377   if (depth == svn_depth_unknown)
12378     depth = svn_depth_infinity;
12379
12380   SVN_WC__DB_WITH_TXN(
12381     bump_revisions_post_update(wcroot, local_relpath, db,
12382                                depth, new_repos_relpath, new_repos_root_url,
12383                                new_repos_uuid, new_revision,
12384                                exclude_relpaths, wcroot_iprops, empty_update,
12385                                notify_func, notify_baton, scratch_pool),
12386     wcroot);
12387
12388   return SVN_NO_ERROR;
12389 }
12390
12391 /* The body of svn_wc__db_lock_add().
12392  */
12393 static svn_error_t *
12394 lock_add_txn(svn_wc__db_wcroot_t *wcroot,
12395              const char *local_relpath,
12396              const svn_wc__db_lock_t *lock,
12397              apr_pool_t *scratch_pool)
12398 {
12399   svn_sqlite__stmt_t *stmt;
12400   const char *repos_relpath;
12401   apr_int64_t repos_id;
12402
12403   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12404                                             &repos_relpath, &repos_id,
12405                                             NULL, NULL, NULL, NULL, NULL,
12406                                             NULL, NULL, NULL, NULL, NULL,
12407                                             wcroot, local_relpath,
12408                                             scratch_pool, scratch_pool));
12409
12410   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_LOCK));
12411   SVN_ERR(svn_sqlite__bindf(stmt, "iss",
12412                             repos_id, repos_relpath, lock->token));
12413
12414   if (lock->owner != NULL)
12415     SVN_ERR(svn_sqlite__bind_text(stmt, 4, lock->owner));
12416
12417   if (lock->comment != NULL)
12418     SVN_ERR(svn_sqlite__bind_text(stmt, 5, lock->comment));
12419
12420   if (lock->date != 0)
12421     SVN_ERR(svn_sqlite__bind_int64(stmt, 6, lock->date));
12422
12423   SVN_ERR(svn_sqlite__insert(NULL, stmt));
12424
12425   return SVN_NO_ERROR;
12426 }
12427
12428
12429 svn_error_t *
12430 svn_wc__db_lock_add(svn_wc__db_t *db,
12431                     const char *local_abspath,
12432                     const svn_wc__db_lock_t *lock,
12433                     apr_pool_t *scratch_pool)
12434 {
12435   svn_wc__db_wcroot_t *wcroot;
12436   const char *local_relpath;
12437
12438   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12439   SVN_ERR_ASSERT(lock != NULL);
12440
12441   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12442                               local_abspath, scratch_pool, scratch_pool));
12443   VERIFY_USABLE_WCROOT(wcroot);
12444
12445   SVN_WC__DB_WITH_TXN(
12446     lock_add_txn(wcroot, local_relpath, lock, scratch_pool),
12447     wcroot);
12448
12449   /* There may be some entries, and the lock info is now out of date.  */
12450   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
12451
12452   return SVN_NO_ERROR;
12453 }
12454
12455
12456 /* The body of svn_wc__db_lock_remove().
12457  */
12458 static svn_error_t *
12459 lock_remove_txn(svn_wc__db_wcroot_t *wcroot,
12460                 const char *local_relpath,
12461                 svn_skel_t *work_items,
12462                 apr_pool_t *scratch_pool)
12463 {
12464   const char *repos_relpath;
12465   apr_int64_t repos_id;
12466   svn_sqlite__stmt_t *stmt;
12467
12468   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12469                                             &repos_relpath, &repos_id,
12470                                             NULL, NULL, NULL, NULL, NULL,
12471                                             NULL, NULL, NULL, NULL, NULL,
12472                                             wcroot, local_relpath,
12473                                             scratch_pool, scratch_pool));
12474
12475   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12476                                     STMT_DELETE_LOCK));
12477   SVN_ERR(svn_sqlite__bindf(stmt, "is", repos_id, repos_relpath));
12478
12479   SVN_ERR(svn_sqlite__step_done(stmt));
12480
12481   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
12482
12483   return SVN_NO_ERROR;
12484 }
12485
12486
12487 svn_error_t *
12488 svn_wc__db_lock_remove(svn_wc__db_t *db,
12489                        const char *local_abspath,
12490                        svn_skel_t *work_items,
12491                        apr_pool_t *scratch_pool)
12492 {
12493   svn_wc__db_wcroot_t *wcroot;
12494   const char *local_relpath;
12495
12496   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12497
12498   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12499                               local_abspath, scratch_pool, scratch_pool));
12500   VERIFY_USABLE_WCROOT(wcroot);
12501
12502   SVN_WC__DB_WITH_TXN(
12503     lock_remove_txn(wcroot, local_relpath, work_items, scratch_pool),
12504     wcroot);
12505
12506   /* There may be some entries, and the lock info is now out of date.  */
12507   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
12508
12509   return SVN_NO_ERROR;
12510 }
12511
12512 /* A helper for scan_addition().
12513  * Compute moved-from information for the node at LOCAL_RELPATH which
12514  * has been determined as having been moved-here.
12515  * If MOVED_FROM_RELPATH is not NULL, set *MOVED_FROM_RELPATH to the
12516  * path of the move-source node in *MOVED_FROM_RELPATH.
12517  * If DELETE_OP_ROOT_RELPATH is not NULL, set *DELETE_OP_ROOT_RELPATH
12518  * to the path of the op-root of the delete-half of the move.
12519  * If moved-from information cannot be derived, set both *MOVED_FROM_RELPATH
12520  * and *DELETE_OP_ROOT_RELPATH to NULL, and return a "copied" status.
12521  * COPY_OPT_ROOT_RELPATH is the relpath of the op-root of the copied-half
12522  * of the move. */
12523 static svn_error_t *
12524 get_moved_from_info(const char **moved_from_relpath,
12525                     const char **moved_from_op_root_relpath,
12526                     const char *moved_to_op_root_relpath,
12527                     int *op_depth,
12528                     svn_wc__db_wcroot_t *wcroot,
12529                     const char *local_relpath,
12530                     apr_pool_t *result_pool,
12531                     apr_pool_t *scratch_pool)
12532 {
12533   svn_sqlite__stmt_t *stmt;
12534   svn_boolean_t have_row;
12535
12536   /* Run a query to get the moved-from path from the DB. */
12537   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12538                                     STMT_SELECT_MOVED_FROM_RELPATH));
12539   SVN_ERR(svn_sqlite__bindf(stmt, "is",
12540                             wcroot->wc_id, moved_to_op_root_relpath));
12541   SVN_ERR(svn_sqlite__step(&have_row, stmt));
12542
12543   if (!have_row)
12544     {
12545       /* The move was only recorded at the copy-half, possibly because
12546        * the move operation was interrupted mid-way between the copy
12547        * and the delete. Treat this node as a normal copy. */
12548       if (moved_from_relpath)
12549         *moved_from_relpath = NULL;
12550       if (moved_from_op_root_relpath)
12551         *moved_from_op_root_relpath = NULL;
12552
12553       SVN_ERR(svn_sqlite__reset(stmt));
12554       return SVN_NO_ERROR;
12555     }
12556
12557   if (op_depth)
12558     *op_depth = svn_sqlite__column_int(stmt, 1);
12559
12560   if (moved_from_relpath || moved_from_op_root_relpath)
12561     {
12562       const char *db_delete_op_root_relpath;
12563
12564       /* The moved-from path from the DB is the relpath of
12565        * the op_root of the delete-half of the move. */
12566       db_delete_op_root_relpath = svn_sqlite__column_text(stmt, 0,
12567                                                           result_pool);
12568       if (moved_from_op_root_relpath)
12569         *moved_from_op_root_relpath = db_delete_op_root_relpath;
12570
12571       if (moved_from_relpath)
12572         {
12573           if (strcmp(moved_to_op_root_relpath, local_relpath) == 0)
12574             {
12575               /* LOCAL_RELPATH is the op_root of the copied-half of the
12576                * move, so the correct MOVED_FROM_ABSPATH is the op-root
12577                * of the delete-half. */
12578               *moved_from_relpath = db_delete_op_root_relpath;
12579             }
12580           else
12581             {
12582               const char *child_relpath;
12583
12584               /* LOCAL_RELPATH is a child that was copied along with the
12585                * op_root of the copied-half of the move. Construct the
12586                * corresponding path beneath the op_root of the delete-half. */
12587
12588               /* Grab the child path relative to the op_root of the move
12589                * destination. */
12590               child_relpath = svn_relpath_skip_ancestor(
12591                                 moved_to_op_root_relpath, local_relpath);
12592
12593               SVN_ERR_ASSERT(child_relpath && strlen(child_relpath) > 0);
12594
12595               /* This join is valid because LOCAL_RELPATH has not been moved
12596                * within the copied-half of the move yet -- else, it would
12597                * be its own op_root. */
12598               *moved_from_relpath = svn_relpath_join(db_delete_op_root_relpath,
12599                                                      child_relpath,
12600                                                      result_pool);
12601             }
12602         }
12603     }
12604
12605   SVN_ERR(svn_sqlite__reset(stmt));
12606
12607   return SVN_NO_ERROR;
12608 }
12609
12610 /* Like svn_wc__db_scan_addition(), but with WCROOT+LOCAL_RELPATH instead of
12611    DB+LOCAL_ABSPATH.
12612
12613    The output value of *ORIGINAL_REPOS_ID will be INVALID_REPOS_ID if there
12614    is no 'copy-from' repository.  */
12615 static svn_error_t *
12616 scan_addition(svn_wc__db_status_t *status,
12617               const char **op_root_relpath_p,
12618               const char **repos_relpath,
12619               apr_int64_t *repos_id,
12620               const char **original_repos_relpath,
12621               apr_int64_t *original_repos_id,
12622               svn_revnum_t *original_revision,
12623               const char **moved_from_relpath,
12624               const char **moved_from_op_root_relpath,
12625               int *moved_from_op_depth,
12626               svn_wc__db_wcroot_t *wcroot,
12627               const char *local_relpath,
12628               apr_pool_t *result_pool,
12629               apr_pool_t *scratch_pool)
12630 {
12631   const char *op_root_relpath;
12632   const char *build_relpath = "";
12633
12634   /* Initialize most of the OUT parameters. Generally, we'll only be filling
12635      in a subset of these, so it is easier to init all up front. Note that
12636      the STATUS parameter will be initialized once we read the status of
12637      the specified node.  */
12638   if (op_root_relpath_p)
12639     *op_root_relpath_p = NULL;
12640   if (original_repos_relpath)
12641     *original_repos_relpath = NULL;
12642   if (original_repos_id)
12643     *original_repos_id = INVALID_REPOS_ID;
12644   if (original_revision)
12645     *original_revision = SVN_INVALID_REVNUM;
12646   if (moved_from_relpath)
12647     *moved_from_relpath = NULL;
12648   if (moved_from_op_root_relpath)
12649     *moved_from_op_root_relpath = NULL;
12650   if (moved_from_op_depth)
12651     *moved_from_op_depth = 0;
12652
12653   {
12654     svn_sqlite__stmt_t *stmt;
12655     svn_boolean_t have_row;
12656     svn_wc__db_status_t presence;
12657     int op_depth;
12658     const char *repos_prefix_path;
12659
12660     /* ### is it faster to fetch fewer columns? */
12661     SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
12662                                       STMT_SELECT_WORKING_NODE));
12663     SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
12664     SVN_ERR(svn_sqlite__step(&have_row, stmt));
12665
12666     if (!have_row)
12667       {
12668         /* Reset statement before returning */
12669         SVN_ERR(svn_sqlite__reset(stmt));
12670
12671         /* ### maybe we should return a usage error instead?  */
12672         return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12673                                  _("The node '%s' was not found."),
12674                                  path_for_error_message(wcroot,
12675                                                         local_relpath,
12676                                                         scratch_pool));
12677       }
12678
12679     presence = svn_sqlite__column_token(stmt, 1, presence_map);
12680
12681     /* The starting node should exist normally.  */
12682     op_depth = svn_sqlite__column_int(stmt, 0);
12683     if (op_depth == 0 || (presence != svn_wc__db_status_normal
12684                           && presence != svn_wc__db_status_incomplete))
12685       /* reset the statement as part of the error generation process */
12686       return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS,
12687                                svn_sqlite__reset(stmt),
12688                                _("Expected node '%s' to be added."),
12689                                path_for_error_message(wcroot,
12690                                                       local_relpath,
12691                                                       scratch_pool));
12692
12693     if (original_revision)
12694       *original_revision = svn_sqlite__column_revnum(stmt, 12);
12695
12696     /* Provide the default status; we'll override as appropriate. */
12697     if (status)
12698       {
12699         if (presence == svn_wc__db_status_normal)
12700           *status = svn_wc__db_status_added;
12701         else
12702           *status = svn_wc__db_status_incomplete;
12703       }
12704
12705
12706     /* Calculate the op root local path components */
12707     op_root_relpath = svn_relpath_prefix(local_relpath, op_depth,
12708                                          scratch_pool);
12709     repos_prefix_path = svn_relpath_skip_ancestor(op_root_relpath,
12710                                                   local_relpath);
12711
12712     if (op_root_relpath_p)
12713       *op_root_relpath_p = apr_pstrdup(result_pool, op_root_relpath);
12714
12715     /* ### This if-statement is quite redundant.
12716      * ### We're checking all these values again within the body anyway.
12717      * ### The body should be broken up appropriately and move into the
12718      * ### outer scope. */
12719     if (original_repos_relpath
12720         || original_repos_id
12721         || (original_revision
12722                 && *original_revision == SVN_INVALID_REVNUM)
12723         || status
12724         || moved_from_relpath || moved_from_op_root_relpath)
12725       {
12726         if (local_relpath != op_root_relpath)
12727           /* requery to get the add/copy root */
12728           {
12729             SVN_ERR(svn_sqlite__reset(stmt));
12730
12731             SVN_ERR(svn_sqlite__bindf(stmt, "is",
12732                                       wcroot->wc_id, op_root_relpath));
12733             SVN_ERR(svn_sqlite__step(&have_row, stmt));
12734
12735             if (!have_row)
12736               {
12737                 /* Reset statement before returning */
12738                 SVN_ERR(svn_sqlite__reset(stmt));
12739
12740                 /* ### maybe we should return a usage error instead?  */
12741                 return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
12742                                          _("The node '%s' was not found."),
12743                                          path_for_error_message(wcroot,
12744                                                                 op_root_relpath,
12745                                                                 scratch_pool));
12746               }
12747
12748             if (original_revision
12749                     && *original_revision == SVN_INVALID_REVNUM)
12750               *original_revision = svn_sqlite__column_revnum(stmt, 12);
12751           }
12752
12753         if (original_repos_relpath)
12754           *original_repos_relpath = svn_sqlite__column_text(stmt, 11,
12755                                                             result_pool);
12756
12757         if (!svn_sqlite__column_is_null(stmt, 10)
12758             && (status
12759                 || original_repos_id
12760                 || moved_from_relpath || moved_from_op_root_relpath))
12761           /* If column 10 (original_repos_id) is NULL,
12762              this is a plain add, not a copy or a move */
12763           {
12764             svn_boolean_t moved_here;
12765             if (original_repos_id)
12766               *original_repos_id = svn_sqlite__column_int64(stmt, 10);
12767
12768             moved_here = svn_sqlite__column_boolean(stmt, 13 /* moved_here */);
12769             if (status)
12770               *status = moved_here ? svn_wc__db_status_moved_here
12771                                    : svn_wc__db_status_copied;
12772
12773             if (moved_here
12774                 && (moved_from_relpath || moved_from_op_root_relpath))
12775               {
12776                 svn_error_t *err;
12777
12778                 err = get_moved_from_info(moved_from_relpath,
12779                                           moved_from_op_root_relpath,
12780                                           op_root_relpath,
12781                                           moved_from_op_depth,
12782                                           wcroot, local_relpath,
12783                                           result_pool,
12784                                           scratch_pool);
12785
12786                 if (err)
12787                   return svn_error_compose_create(
12788                                 err, svn_sqlite__reset(stmt));
12789               }
12790           }
12791       }
12792
12793
12794     /* ### This loop here is to skip up to the first node which is a BASE node,
12795        because base_get_info() doesn't accommodate the scenario that
12796        we're looking at here; we found the true op_root, which may be inside
12797        further changed trees. */
12798     if (repos_relpath || repos_id)
12799       {
12800         const char *base_relpath;
12801
12802         while (TRUE)
12803           {
12804             const char *tmp;
12805
12806             SVN_ERR(svn_sqlite__reset(stmt));
12807
12808             /* Pointing at op_depth, look at the parent */
12809             repos_prefix_path =
12810                 svn_relpath_join(svn_relpath_basename(op_root_relpath, NULL),
12811                                  repos_prefix_path,
12812                                  scratch_pool);
12813             op_root_relpath = svn_relpath_dirname(op_root_relpath, scratch_pool);
12814
12815
12816             SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, op_root_relpath));
12817             SVN_ERR(svn_sqlite__step(&have_row, stmt));
12818
12819             if (! have_row)
12820               break;
12821
12822             op_depth = svn_sqlite__column_int(stmt, 0);
12823
12824             /* Skip to op_depth */
12825             tmp = op_root_relpath;
12826
12827             op_root_relpath = svn_relpath_prefix(op_root_relpath, op_depth,
12828                                                  scratch_pool);
12829             repos_prefix_path = svn_relpath_join(
12830                                                  svn_relpath_skip_ancestor(op_root_relpath, tmp),
12831                                                  repos_prefix_path, scratch_pool);
12832           }
12833
12834       SVN_ERR(svn_sqlite__reset(stmt));
12835
12836       build_relpath = repos_prefix_path;
12837
12838       /* If we're here, then we have an added/copied/moved (start) node, and
12839          CURRENT_ABSPATH now points to a BASE node. Figure out the repository
12840          information for the current node, and use that to compute the start
12841          node's repository information.  */
12842       SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
12843                                                 &base_relpath, repos_id,
12844                                                 NULL, NULL, NULL, NULL, NULL,
12845                                                 NULL, NULL, NULL, NULL, NULL,
12846                                                 wcroot, op_root_relpath,
12847                                                 scratch_pool, scratch_pool));
12848
12849         if (repos_relpath)
12850           *repos_relpath = svn_relpath_join(base_relpath, build_relpath,
12851                                             result_pool);
12852       }
12853     else
12854       SVN_ERR(svn_sqlite__reset(stmt));
12855   }
12856   /* Postconditions */
12857 #ifdef SVN_DEBUG
12858   if (status)
12859     {
12860       SVN_ERR_ASSERT(*status == svn_wc__db_status_added
12861                      || *status == svn_wc__db_status_copied
12862                      || *status == svn_wc__db_status_incomplete
12863                      || *status == svn_wc__db_status_moved_here);
12864       if (*status == svn_wc__db_status_added)
12865         {
12866           SVN_ERR_ASSERT(!original_repos_relpath
12867                          || *original_repos_relpath == NULL);
12868           SVN_ERR_ASSERT(!original_revision
12869                          || *original_revision == SVN_INVALID_REVNUM);
12870           SVN_ERR_ASSERT(!original_repos_id
12871                          || *original_repos_id == INVALID_REPOS_ID);
12872         }
12873       /* An upgrade with a missing directory can leave INCOMPLETE working
12874          op-roots. See upgrade_tests.py 29: upgrade with missing replaced dir
12875        */
12876       else if (*status != svn_wc__db_status_incomplete)
12877         {
12878           SVN_ERR_ASSERT(!original_repos_relpath
12879                          || *original_repos_relpath != NULL);
12880           SVN_ERR_ASSERT(!original_revision
12881                          || *original_revision != SVN_INVALID_REVNUM);
12882           SVN_ERR_ASSERT(!original_repos_id
12883                          || *original_repos_id != INVALID_REPOS_ID);
12884         }
12885     }
12886   SVN_ERR_ASSERT(!op_root_relpath_p || *op_root_relpath_p != NULL);
12887 #endif
12888
12889   return SVN_NO_ERROR;
12890 }
12891
12892 svn_error_t *
12893 svn_wc__db_scan_addition_internal(
12894               svn_wc__db_status_t *status,
12895               const char **op_root_relpath_p,
12896               const char **repos_relpath,
12897               apr_int64_t *repos_id,
12898               const char **original_repos_relpath,
12899               apr_int64_t *original_repos_id,
12900               svn_revnum_t *original_revision,
12901               svn_wc__db_wcroot_t *wcroot,
12902               const char *local_relpath,
12903               apr_pool_t *result_pool,
12904               apr_pool_t *scratch_pool)
12905 {
12906   return svn_error_trace(
12907       scan_addition(status, op_root_relpath_p, repos_relpath, repos_id,
12908                     original_repos_relpath, original_repos_id,
12909                     original_revision, NULL, NULL, NULL,
12910                     wcroot, local_relpath, result_pool, scratch_pool));
12911 }
12912
12913 svn_error_t *
12914 svn_wc__db_scan_addition(svn_wc__db_status_t *status,
12915                          const char **op_root_abspath,
12916                          const char **repos_relpath,
12917                          const char **repos_root_url,
12918                          const char **repos_uuid,
12919                          const char **original_repos_relpath,
12920                          const char **original_root_url,
12921                          const char **original_uuid,
12922                          svn_revnum_t *original_revision,
12923                          svn_wc__db_t *db,
12924                          const char *local_abspath,
12925                          apr_pool_t *result_pool,
12926                          apr_pool_t *scratch_pool)
12927 {
12928   svn_wc__db_wcroot_t *wcroot;
12929   const char *local_relpath;
12930   const char *op_root_relpath = NULL;
12931   apr_int64_t repos_id = INVALID_REPOS_ID;
12932   apr_int64_t original_repos_id = INVALID_REPOS_ID;
12933   apr_int64_t *repos_id_p
12934     = (repos_root_url || repos_uuid) ? &repos_id : NULL;
12935   apr_int64_t *original_repos_id_p
12936     = (original_root_url || original_uuid) ? &original_repos_id : NULL;
12937
12938   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12939
12940   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12941                               local_abspath, scratch_pool, scratch_pool));
12942   VERIFY_USABLE_WCROOT(wcroot);
12943
12944   SVN_WC__DB_WITH_TXN4(
12945           scan_addition(status,
12946                         op_root_abspath
12947                                 ? &op_root_relpath
12948                                 : NULL,
12949                         repos_relpath, repos_id_p,
12950                         original_repos_relpath, original_repos_id_p,
12951                         original_revision,
12952                         NULL, NULL, NULL,
12953                         wcroot, local_relpath, result_pool, scratch_pool),
12954           svn_wc__db_fetch_repos_info(repos_root_url, repos_uuid, wcroot,
12955                                       repos_id, result_pool),
12956           svn_wc__db_fetch_repos_info(original_root_url, original_uuid,
12957                                       wcroot, original_repos_id,
12958                                       result_pool),
12959           SVN_NO_ERROR,
12960           wcroot);
12961
12962   if (op_root_abspath)
12963     *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
12964                                        result_pool);
12965   /* REPOS_ID must be valid if requested; ORIGINAL_REPOS_ID need not be. */
12966   SVN_ERR_ASSERT(repos_id_p == NULL || repos_id != INVALID_REPOS_ID);
12967
12968   return SVN_NO_ERROR;
12969 }
12970
12971 svn_error_t *
12972 svn_wc__db_scan_moved(const char **moved_from_abspath,
12973                       const char **op_root_abspath,
12974                       const char **op_root_moved_from_abspath,
12975                       const char **moved_from_delete_abspath,
12976                       svn_wc__db_t *db,
12977                       const char *local_abspath,
12978                       apr_pool_t *result_pool,
12979                       apr_pool_t *scratch_pool)
12980 {
12981   svn_wc__db_wcroot_t *wcroot;
12982   const char *local_relpath;
12983   svn_wc__db_status_t status;
12984   const char *op_root_relpath = NULL;
12985   const char *moved_from_relpath = NULL;
12986   const char *moved_from_op_root_relpath = NULL;
12987   int moved_from_op_depth = -1;
12988
12989   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
12990
12991   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
12992                               local_abspath, scratch_pool, scratch_pool));
12993   VERIFY_USABLE_WCROOT(wcroot);
12994
12995   SVN_WC__DB_WITH_TXN(
12996           scan_addition(&status,
12997                         op_root_abspath
12998                                 ? &op_root_relpath
12999                                 : NULL,
13000                         NULL, NULL,
13001                         NULL, NULL, NULL,
13002                         moved_from_abspath
13003                             ? &moved_from_relpath
13004                             : NULL,
13005                         (op_root_moved_from_abspath
13006                          || moved_from_delete_abspath)
13007                             ? &moved_from_op_root_relpath
13008                             : NULL,
13009                         moved_from_delete_abspath
13010                             ? &moved_from_op_depth
13011                             : NULL,
13012                         wcroot, local_relpath, scratch_pool, scratch_pool),
13013           wcroot);
13014
13015   if (status != svn_wc__db_status_moved_here || !moved_from_relpath)
13016     return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
13017                              _("Path '%s' was not moved here"),
13018                              path_for_error_message(wcroot, local_relpath,
13019                                                     scratch_pool));
13020
13021   if (op_root_abspath)
13022     *op_root_abspath = svn_dirent_join(wcroot->abspath, op_root_relpath,
13023                                        result_pool);
13024
13025   if (moved_from_abspath)
13026     *moved_from_abspath = svn_dirent_join(wcroot->abspath, moved_from_relpath,
13027                                           result_pool);
13028
13029   if (op_root_moved_from_abspath)
13030     *op_root_moved_from_abspath = svn_dirent_join(wcroot->abspath,
13031                                                   moved_from_op_root_relpath,
13032                                                   result_pool);
13033
13034   /* The deleted node is either where we moved from, or one of its ancestors */
13035   if (moved_from_delete_abspath)
13036     {
13037       const char *tmp = svn_relpath_prefix(moved_from_op_root_relpath,
13038                                            moved_from_op_depth, scratch_pool);
13039
13040       *moved_from_delete_abspath = svn_dirent_join(wcroot->abspath, tmp,
13041                                                    scratch_pool);
13042     }
13043
13044   return SVN_NO_ERROR;
13045 }
13046
13047 /* ### Recursive helper for svn_wc__db_follow_moved_to()
13048  */
13049 static svn_error_t *
13050 follow_moved_to(svn_wc__db_wcroot_t *wcroot,
13051                 const char *local_relpath,
13052                 int op_depth,
13053                 apr_array_header_t **moved_tos,
13054                 apr_pool_t *result_pool,
13055                 apr_pool_t *scratch_pool)
13056 {
13057   svn_sqlite__stmt_t *stmt;
13058   svn_boolean_t have_row;
13059   int shadowing_op_depth;
13060   const char *ancestor_relpath;
13061   const char *node_moved_to = NULL;
13062   int i;
13063
13064   /* Obtain the depth of the node directly shadowing local_relpath
13065      as it exists at OP_DEPTH, and perhaps moved to info */
13066   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13067                                     STMT_SELECT_OP_DEPTH_MOVED_TO));
13068   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
13069                             op_depth));
13070   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13071   if (have_row)
13072     {
13073       shadowing_op_depth = svn_sqlite__column_int(stmt, 0);
13074       node_moved_to = svn_sqlite__column_text(stmt, 1, result_pool);
13075
13076       if (node_moved_to)
13077         {
13078           struct svn_wc__db_moved_to_t *moved_to;
13079
13080           moved_to = apr_palloc(result_pool, sizeof(*moved_to));
13081           moved_to->op_depth = shadowing_op_depth;
13082           moved_to->local_relpath = node_moved_to;
13083           APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
13084         }
13085     }
13086
13087   SVN_ERR(svn_sqlite__reset(stmt));
13088
13089   if (!have_row)
13090     {
13091       /* Node is not shadowed, so not moved */
13092       return SVN_NO_ERROR;
13093     }
13094   else if (node_moved_to)
13095     {
13096       /* Moved directly, so we have the final location */
13097       return SVN_NO_ERROR;
13098     }
13099   /* Need to handle being moved via an ancestor. */
13100   ancestor_relpath = local_relpath;
13101   for (i = relpath_depth(local_relpath); i > shadowing_op_depth; --i)
13102     {
13103       const char *ancestor_moved_to;
13104
13105       ancestor_relpath = svn_relpath_dirname(ancestor_relpath, scratch_pool);
13106
13107       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13108                                         STMT_SELECT_MOVED_TO));
13109       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, ancestor_relpath,
13110                                 shadowing_op_depth));
13111       SVN_ERR(svn_sqlite__step_row(stmt));
13112
13113       ancestor_moved_to = svn_sqlite__column_text(stmt, 0, scratch_pool);
13114       SVN_ERR(svn_sqlite__reset(stmt));
13115       if (ancestor_moved_to)
13116         {
13117           struct svn_wc__db_moved_to_t *moved_to;
13118
13119           node_moved_to
13120               = svn_relpath_join(ancestor_moved_to,
13121                                  svn_relpath_skip_ancestor(ancestor_relpath,
13122                                                            local_relpath),
13123                                  result_pool);
13124
13125           moved_to = apr_palloc(result_pool, sizeof(*moved_to));
13126           moved_to->op_depth = shadowing_op_depth;
13127           moved_to->local_relpath = node_moved_to;
13128           APR_ARRAY_PUSH(*moved_tos, struct svn_wc__db_moved_to_t *) = moved_to;
13129
13130           SVN_ERR(follow_moved_to(wcroot, node_moved_to,
13131                                   relpath_depth(ancestor_moved_to),
13132                                   moved_tos, result_pool, scratch_pool));
13133
13134           break;
13135         }
13136     }
13137
13138   return SVN_NO_ERROR;
13139 }
13140
13141 svn_error_t *
13142 svn_wc__db_follow_moved_to(apr_array_header_t **moved_tos,
13143                            svn_wc__db_t *db,
13144                            const char *local_abspath,
13145                            apr_pool_t *result_pool,
13146                            apr_pool_t *scratch_pool)
13147 {
13148   svn_wc__db_wcroot_t *wcroot;
13149   const char *local_relpath;
13150
13151   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13152
13153   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13154                               local_abspath, scratch_pool, scratch_pool));
13155   VERIFY_USABLE_WCROOT(wcroot);
13156
13157   *moved_tos = apr_array_make(result_pool, 0,
13158                               sizeof(struct svn_wc__db_moved_to_t *));
13159
13160   /* ### Wrap in a transaction */
13161   SVN_WC__DB_WITH_TXN(follow_moved_to(wcroot, local_relpath, 0, moved_tos,
13162                                       result_pool, scratch_pool),
13163                       wcroot);
13164
13165   /* ### Convert moved_to to abspath */
13166
13167   return SVN_NO_ERROR;
13168 }
13169
13170 svn_error_t *
13171 svn_wc__db_scan_moved_to_internal(const char **move_src_relpath,
13172                                   const char **move_dst_relpath,
13173                                   const char **delete_relpath,
13174                                   svn_wc__db_wcroot_t *wcroot,
13175                                   const char *local_relpath,
13176                                   int op_depth,
13177                                   apr_pool_t *result_pool,
13178                                   apr_pool_t *scratch_pool)
13179 {
13180   svn_sqlite__stmt_t *stmt;
13181   svn_boolean_t have_row;
13182   int delete_op_depth;
13183   const char *relpath = local_relpath;
13184   const char *dst_relpath;
13185
13186   SVN_ERR_ASSERT(local_relpath[0]); /* Not valid on the WC root */
13187
13188   if (move_src_relpath)
13189     *move_src_relpath = NULL;
13190   if (move_dst_relpath)
13191     *move_dst_relpath = NULL;
13192   if (delete_relpath)
13193     *delete_relpath = NULL;
13194
13195   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13196                                     STMT_SELECT_OP_DEPTH_MOVED_TO));
13197   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath, op_depth));
13198
13199   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13200
13201   if (!have_row)
13202     {
13203       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND,
13204                                svn_sqlite__reset(stmt),
13205                                _("Node '%s' is not shadowed"),
13206                                path_for_error_message(wcroot, local_relpath,
13207                                                       scratch_pool));
13208     }
13209
13210   delete_op_depth = svn_sqlite__column_int(stmt, 0);
13211   dst_relpath = svn_sqlite__column_text(stmt, 1, scratch_pool);
13212
13213   SVN_ERR(svn_sqlite__reset(stmt));
13214
13215   while (!dst_relpath && have_row)
13216     {
13217       relpath = svn_relpath_dirname(relpath, scratch_pool);
13218
13219       if (relpath_depth(relpath) < delete_op_depth)
13220         break;
13221
13222       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13223                                         STMT_SELECT_DEPTH_NODE));
13224       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, relpath,
13225                                 delete_op_depth));
13226
13227       SVN_ERR(svn_sqlite__step(&have_row, stmt));
13228
13229       if (have_row)
13230         dst_relpath = svn_sqlite__column_text(stmt, 13, scratch_pool);
13231
13232       SVN_ERR(svn_sqlite__reset(stmt));
13233     }
13234
13235   if (dst_relpath)
13236     {
13237       if (move_src_relpath)
13238         *move_src_relpath = apr_pstrdup(result_pool, relpath);
13239
13240       if (move_dst_relpath)
13241         *move_dst_relpath = apr_pstrdup(result_pool, dst_relpath);
13242
13243       if (delete_relpath)
13244         *delete_relpath = svn_relpath_prefix(local_relpath, delete_op_depth,
13245                                              result_pool);
13246     }
13247
13248   return SVN_NO_ERROR;
13249 }
13250
13251 /* Public (within libsvn_wc) absolute path version of
13252    svn_wc__db_op_depth_moved_to with the op-depth hard-coded to
13253    BASE. */
13254 svn_error_t *
13255 svn_wc__db_base_moved_to(const char **move_dst_abspath,
13256                          const char **move_dst_op_root_abspath,
13257                          const char **move_src_root_abspath,
13258                          const char **delete_abspath,
13259                          svn_wc__db_t *db,
13260                          const char *local_abspath,
13261                          apr_pool_t *result_pool,
13262                          apr_pool_t *scratch_pool)
13263 {
13264   svn_wc__db_wcroot_t *wcroot;
13265   const char *local_relpath;
13266   const char *dst_root_relpath;
13267   const char *src_root_relpath, *delete_relpath;
13268
13269   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13270
13271
13272   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13273                               local_abspath, scratch_pool, scratch_pool));
13274   VERIFY_USABLE_WCROOT(wcroot);
13275
13276   SVN_WC__DB_WITH_TXN(svn_wc__db_scan_moved_to_internal(&src_root_relpath,
13277                                                         &dst_root_relpath,
13278                                                         &delete_relpath,
13279                                                         wcroot, local_relpath,
13280                                                         0 /* BASE */,
13281                                                         scratch_pool,
13282                                                         scratch_pool),
13283                       wcroot);
13284
13285   if (move_dst_abspath)
13286     *move_dst_abspath =
13287         dst_root_relpath
13288           ? svn_dirent_join(wcroot->abspath,
13289                             svn_dirent_join(
13290                                     dst_root_relpath,
13291                                     svn_relpath_skip_ancestor(src_root_relpath,
13292                                                               local_relpath),
13293                                     scratch_pool),
13294                             result_pool)
13295           : NULL;
13296
13297   if (move_dst_op_root_abspath)
13298     *move_dst_op_root_abspath =
13299           dst_root_relpath
13300               ? svn_dirent_join(wcroot->abspath, dst_root_relpath, result_pool)
13301               : NULL;
13302
13303   if (move_src_root_abspath)
13304     *move_src_root_abspath =
13305           src_root_relpath
13306               ? svn_dirent_join(wcroot->abspath, src_root_relpath, result_pool)
13307               : NULL;
13308
13309   if (delete_abspath)
13310     *delete_abspath =
13311           delete_relpath
13312               ? svn_dirent_join(wcroot->abspath, delete_relpath, result_pool)
13313               : NULL;
13314
13315   return SVN_NO_ERROR;
13316 }
13317
13318 svn_error_t *
13319 svn_wc__db_upgrade_begin(svn_sqlite__db_t **sdb,
13320                          apr_int64_t *repos_id,
13321                          apr_int64_t *wc_id,
13322                          svn_wc__db_t *wc_db,
13323                          const char *dir_abspath,
13324                          const char *repos_root_url,
13325                          const char *repos_uuid,
13326                          apr_pool_t *scratch_pool)
13327 {
13328   svn_wc__db_wcroot_t *wcroot;
13329
13330   /* Upgrade is inherently exclusive so specify exclusive locking. */
13331   SVN_ERR(create_db(sdb, repos_id, wc_id, dir_abspath,
13332                     repos_root_url, repos_uuid,
13333                     SDB_FILE,
13334                     NULL, SVN_INVALID_REVNUM, svn_depth_unknown,
13335                     TRUE /* exclusive */,
13336                     0 /* timeout */,
13337                     wc_db->state_pool, scratch_pool));
13338
13339   SVN_ERR(svn_wc__db_pdh_create_wcroot(&wcroot,
13340                                        apr_pstrdup(wc_db->state_pool,
13341                                                    dir_abspath),
13342                                        *sdb, *wc_id, FORMAT_FROM_SDB,
13343                                        FALSE /* auto-upgrade */,
13344                                        wc_db->state_pool, scratch_pool));
13345
13346   /* The WCROOT is complete. Stash it into DB.  */
13347   svn_hash_sets(wc_db->dir_data, wcroot->abspath, wcroot);
13348
13349   return SVN_NO_ERROR;
13350 }
13351
13352 svn_error_t *
13353 svn_wc__db_upgrade_insert_external(svn_wc__db_t *db,
13354                                    const char *local_abspath,
13355                                    svn_node_kind_t kind,
13356                                    const char *parent_abspath,
13357                                    const char *def_local_abspath,
13358                                    const char *repos_relpath,
13359                                    const char *repos_root_url,
13360                                    const char *repos_uuid,
13361                                    svn_revnum_t def_peg_revision,
13362                                    svn_revnum_t def_revision,
13363                                    apr_pool_t *scratch_pool)
13364 {
13365   svn_wc__db_wcroot_t *wcroot;
13366   const char *def_local_relpath;
13367   svn_sqlite__stmt_t *stmt;
13368   svn_boolean_t have_row;
13369   apr_int64_t repos_id;
13370
13371   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
13372
13373   /* We know only of DEF_LOCAL_ABSPATH that it definitely belongs to "this"
13374    * WC, i.e. where the svn:externals prop is set. The external target path
13375    * itself may be "hidden behind" other working copies. */
13376   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &def_local_relpath,
13377                                                 db, def_local_abspath,
13378                                                 scratch_pool, scratch_pool));
13379   VERIFY_USABLE_WCROOT(wcroot);
13380
13381
13382   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13383                                     STMT_SELECT_REPOSITORY));
13384   SVN_ERR(svn_sqlite__bindf(stmt, "s", repos_root_url));
13385   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13386
13387   if (have_row)
13388     repos_id = svn_sqlite__column_int64(stmt, 0);
13389   SVN_ERR(svn_sqlite__reset(stmt));
13390
13391   if (!have_row)
13392     {
13393       /* Need to set up a new repository row. */
13394       SVN_ERR(create_repos_id(&repos_id, repos_root_url, repos_uuid,
13395                               wcroot->sdb, scratch_pool));
13396     }
13397
13398   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13399                                     STMT_INSERT_EXTERNAL));
13400
13401   /* wc_id, local_relpath, parent_relpath, presence, kind, def_local_relpath,
13402    * repos_id, def_repos_relpath, def_operational_revision, def_revision */
13403   SVN_ERR(svn_sqlite__bindf(stmt, "issstsis",
13404                             wcroot->wc_id,
13405                             svn_dirent_skip_ancestor(wcroot->abspath,
13406                                                      local_abspath),
13407                             svn_dirent_skip_ancestor(wcroot->abspath,
13408                                                      parent_abspath),
13409                             "normal",
13410                             kind_map, kind,
13411                             def_local_relpath,
13412                             repos_id,
13413                             repos_relpath));
13414
13415   if (SVN_IS_VALID_REVNUM(def_peg_revision))
13416     SVN_ERR(svn_sqlite__bind_revnum(stmt, 9, def_peg_revision));
13417
13418   if (SVN_IS_VALID_REVNUM(def_revision))
13419     SVN_ERR(svn_sqlite__bind_revnum(stmt, 10, def_revision));
13420
13421   SVN_ERR(svn_sqlite__insert(NULL, stmt));
13422
13423   return SVN_NO_ERROR;
13424 }
13425
13426 svn_error_t *
13427 svn_wc__db_wq_add_internal(svn_wc__db_wcroot_t *wcroot,
13428                            const svn_skel_t *work_item,
13429                            apr_pool_t *scratch_pool)
13430 {
13431   /* Add the work item(s) to the WORK_QUEUE.  */
13432   return svn_error_trace(add_work_items(wcroot->sdb, work_item,
13433                                         scratch_pool));
13434 }
13435
13436 svn_error_t *
13437 svn_wc__db_wq_add(svn_wc__db_t *db,
13438                   const char *wri_abspath,
13439                   const svn_skel_t *work_item,
13440                   apr_pool_t *scratch_pool)
13441 {
13442   svn_wc__db_wcroot_t *wcroot;
13443   const char *local_relpath;
13444
13445   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13446
13447   /* Quick exit, if there are no work items to queue up.  */
13448   if (work_item == NULL)
13449     return SVN_NO_ERROR;
13450
13451   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13452                               wri_abspath, scratch_pool, scratch_pool));
13453   VERIFY_USABLE_WCROOT(wcroot);
13454
13455   /* Add the work item(s) to the WORK_QUEUE.  */
13456   return svn_error_trace(add_work_items(wcroot->sdb, work_item,
13457                                         scratch_pool));
13458 }
13459
13460 /* The body of svn_wc__db_wq_fetch_next().
13461  */
13462 static svn_error_t *
13463 wq_fetch_next(apr_uint64_t *id,
13464               svn_skel_t **work_item,
13465               svn_wc__db_wcroot_t *wcroot,
13466               const char *local_relpath,
13467               apr_uint64_t completed_id,
13468               apr_pool_t *result_pool,
13469               apr_pool_t *scratch_pool)
13470 {
13471   svn_sqlite__stmt_t *stmt;
13472   svn_boolean_t have_row;
13473
13474   if (completed_id != 0)
13475     {
13476       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13477                                         STMT_DELETE_WORK_ITEM));
13478       SVN_ERR(svn_sqlite__bind_int64(stmt, 1, completed_id));
13479
13480       SVN_ERR(svn_sqlite__step_done(stmt));
13481     }
13482
13483   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13484                                     STMT_SELECT_WORK_ITEM));
13485   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13486
13487   if (!have_row)
13488     {
13489       *id = 0;
13490       *work_item = NULL;
13491     }
13492   else
13493     {
13494       apr_size_t len;
13495       const void *val;
13496
13497       *id = svn_sqlite__column_int64(stmt, 0);
13498
13499       val = svn_sqlite__column_blob(stmt, 1, &len, result_pool);
13500
13501       *work_item = svn_skel__parse(val, len, result_pool);
13502     }
13503
13504   return svn_error_trace(svn_sqlite__reset(stmt));
13505 }
13506
13507 svn_error_t *
13508 svn_wc__db_wq_fetch_next(apr_uint64_t *id,
13509                          svn_skel_t **work_item,
13510                          svn_wc__db_t *db,
13511                          const char *wri_abspath,
13512                          apr_uint64_t completed_id,
13513                          apr_pool_t *result_pool,
13514                          apr_pool_t *scratch_pool)
13515 {
13516   svn_wc__db_wcroot_t *wcroot;
13517   const char *local_relpath;
13518
13519   SVN_ERR_ASSERT(id != NULL);
13520   SVN_ERR_ASSERT(work_item != NULL);
13521   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13522
13523   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13524                               wri_abspath, scratch_pool, scratch_pool));
13525   VERIFY_USABLE_WCROOT(wcroot);
13526
13527   SVN_WC__DB_WITH_TXN(
13528     wq_fetch_next(id, work_item,
13529                   wcroot, local_relpath, completed_id,
13530                   result_pool, scratch_pool),
13531     wcroot);
13532
13533   return SVN_NO_ERROR;
13534 }
13535
13536 /* Records timestamp and date for one or more files in wcroot */
13537 static svn_error_t *
13538 wq_record(svn_wc__db_wcroot_t *wcroot,
13539           apr_hash_t *record_map,
13540           apr_pool_t *scratch_pool)
13541 {
13542   apr_hash_index_t *hi;
13543   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
13544
13545   for (hi = apr_hash_first(scratch_pool, record_map); hi;
13546        hi = apr_hash_next(hi))
13547     {
13548       const char *local_abspath = apr_hash_this_key(hi);
13549       const svn_io_dirent2_t *dirent = apr_hash_this_val(hi);
13550       const char *local_relpath = svn_dirent_skip_ancestor(wcroot->abspath,
13551                                                            local_abspath);
13552
13553       svn_pool_clear(iterpool);
13554
13555       if (! local_relpath)
13556         continue;
13557
13558       SVN_ERR(db_record_fileinfo(wcroot, local_relpath,
13559                                  dirent->filesize, dirent->mtime,
13560                                  iterpool));
13561     }
13562
13563   svn_pool_destroy(iterpool);
13564   return SVN_NO_ERROR;
13565 }
13566
13567 svn_error_t *
13568 svn_wc__db_wq_record_and_fetch_next(apr_uint64_t *id,
13569                                     svn_skel_t **work_item,
13570                                     svn_wc__db_t *db,
13571                                     const char *wri_abspath,
13572                                     apr_uint64_t completed_id,
13573                                     apr_hash_t *record_map,
13574                                     apr_pool_t *result_pool,
13575                                     apr_pool_t *scratch_pool)
13576 {
13577   svn_wc__db_wcroot_t *wcroot;
13578   const char *local_relpath;
13579
13580   SVN_ERR_ASSERT(id != NULL);
13581   SVN_ERR_ASSERT(work_item != NULL);
13582   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
13583
13584   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13585                               wri_abspath, scratch_pool, scratch_pool));
13586   VERIFY_USABLE_WCROOT(wcroot);
13587
13588   SVN_WC__DB_WITH_TXN(
13589     svn_error_compose_create(
13590             wq_fetch_next(id, work_item,
13591                           wcroot, local_relpath, completed_id,
13592                           result_pool, scratch_pool),
13593             wq_record(wcroot, record_map, scratch_pool)),
13594     wcroot);
13595
13596   return SVN_NO_ERROR;
13597 }
13598
13599
13600
13601 /* ### temporary API. remove before release.  */
13602 svn_error_t *
13603 svn_wc__db_temp_get_format(int *format,
13604                            svn_wc__db_t *db,
13605                            const char *local_dir_abspath,
13606                            apr_pool_t *scratch_pool)
13607 {
13608   svn_wc__db_wcroot_t *wcroot;
13609   const char *local_relpath;
13610   svn_error_t *err;
13611
13612   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13613   /* ### assert that we were passed a directory?  */
13614
13615   err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13616                                 local_dir_abspath, scratch_pool, scratch_pool);
13617
13618   /* If we hit an error examining this directory, then declare this
13619      directory to not be a working copy.  */
13620   if (err)
13621     {
13622       if (err->apr_err != SVN_ERR_WC_NOT_WORKING_COPY)
13623         return svn_error_trace(err);
13624       svn_error_clear(err);
13625
13626       /* Remap the returned error.  */
13627       *format = 0;
13628       return svn_error_createf(SVN_ERR_WC_MISSING, NULL,
13629                                _("'%s' is not a working copy"),
13630                                svn_dirent_local_style(local_dir_abspath,
13631                                                       scratch_pool));
13632     }
13633
13634   SVN_ERR_ASSERT(wcroot != NULL);
13635   SVN_ERR_ASSERT(wcroot->format >= 1);
13636
13637   *format = wcroot->format;
13638
13639   return SVN_NO_ERROR;
13640 }
13641
13642 /* ### temporary API. remove before release.  */
13643 svn_wc_adm_access_t *
13644 svn_wc__db_temp_get_access(svn_wc__db_t *db,
13645                            const char *local_dir_abspath,
13646                            apr_pool_t *scratch_pool)
13647 {
13648   const char *local_relpath;
13649   svn_wc__db_wcroot_t *wcroot;
13650   svn_error_t *err;
13651
13652   SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13653
13654   /* ### we really need to assert that we were passed a directory. sometimes
13655      ### adm_retrieve_internal is asked about a file, and then it asks us
13656      ### for an access baton for it. we should definitely return NULL, but
13657      ### ideally: the caller would never ask us about a non-directory.  */
13658
13659   err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13660                             db, local_dir_abspath, scratch_pool, scratch_pool);
13661   if (err)
13662     {
13663       svn_error_clear(err);
13664       return NULL;
13665     }
13666
13667   if (!wcroot)
13668     return NULL;
13669
13670   return svn_hash_gets(wcroot->access_cache, local_dir_abspath);
13671 }
13672
13673
13674 /* ### temporary API. remove before release.  */
13675 void
13676 svn_wc__db_temp_set_access(svn_wc__db_t *db,
13677                            const char *local_dir_abspath,
13678                            svn_wc_adm_access_t *adm_access,
13679                            apr_pool_t *scratch_pool)
13680 {
13681   const char *local_relpath;
13682   svn_wc__db_wcroot_t *wcroot;
13683   svn_error_t *err;
13684
13685   SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13686   /* ### assert that we were passed a directory?  */
13687
13688   err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13689                             db, local_dir_abspath, scratch_pool, scratch_pool);
13690   if (err)
13691     {
13692       /* We don't even have a wcroot, so just bail. */
13693       svn_error_clear(err);
13694       return;
13695     }
13696
13697   /* Better not override something already there.  */
13698   SVN_ERR_ASSERT_NO_RETURN(
13699     svn_hash_gets(wcroot->access_cache, local_dir_abspath) == NULL
13700   );
13701   svn_hash_sets(wcroot->access_cache, local_dir_abspath, adm_access);
13702 }
13703
13704
13705 /* ### temporary API. remove before release.  */
13706 svn_error_t *
13707 svn_wc__db_temp_close_access(svn_wc__db_t *db,
13708                              const char *local_dir_abspath,
13709                              svn_wc_adm_access_t *adm_access,
13710                              apr_pool_t *scratch_pool)
13711 {
13712   const char *local_relpath;
13713   svn_wc__db_wcroot_t *wcroot;
13714
13715   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13716   /* ### assert that we were passed a directory?  */
13717
13718   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13719                               local_dir_abspath, scratch_pool, scratch_pool));
13720   svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13721
13722   return SVN_NO_ERROR;
13723 }
13724
13725
13726 /* ### temporary API. remove before release.  */
13727 void
13728 svn_wc__db_temp_clear_access(svn_wc__db_t *db,
13729                              const char *local_dir_abspath,
13730                              apr_pool_t *scratch_pool)
13731 {
13732   const char *local_relpath;
13733   svn_wc__db_wcroot_t *wcroot;
13734   svn_error_t *err;
13735
13736   SVN_ERR_ASSERT_NO_RETURN(svn_dirent_is_absolute(local_dir_abspath));
13737   /* ### assert that we were passed a directory?  */
13738
13739   err = svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
13740                             db, local_dir_abspath, scratch_pool, scratch_pool);
13741   if (err)
13742     {
13743       svn_error_clear(err);
13744       return;
13745     }
13746
13747   svn_hash_sets(wcroot->access_cache, local_dir_abspath, NULL);
13748 }
13749
13750
13751 apr_hash_t *
13752 svn_wc__db_temp_get_all_access(svn_wc__db_t *db,
13753                                apr_pool_t *result_pool)
13754 {
13755   apr_hash_t *result = apr_hash_make(result_pool);
13756   apr_hash_index_t *hi;
13757
13758   for (hi = apr_hash_first(result_pool, db->dir_data);
13759        hi;
13760        hi = apr_hash_next(hi))
13761     {
13762       const svn_wc__db_wcroot_t *wcroot = apr_hash_this_val(hi);
13763
13764       /* This is highly redundant, 'cause the same WCROOT will appear many
13765          times in dir_data. */
13766       result = apr_hash_overlay(result_pool, result, wcroot->access_cache);
13767     }
13768
13769   return result;
13770 }
13771
13772
13773 svn_error_t *
13774 svn_wc__db_temp_borrow_sdb(svn_sqlite__db_t **sdb,
13775                            svn_wc__db_t *db,
13776                            const char *local_dir_abspath,
13777                            apr_pool_t *scratch_pool)
13778 {
13779   svn_wc__db_wcroot_t *wcroot;
13780   const char *local_relpath;
13781
13782   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
13783
13784   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13785                             local_dir_abspath, scratch_pool, scratch_pool));
13786   VERIFY_USABLE_WCROOT(wcroot);
13787
13788   *sdb = wcroot->sdb;
13789
13790   return SVN_NO_ERROR;
13791 }
13792
13793
13794 svn_error_t *
13795 svn_wc__db_read_conflict_victims(const apr_array_header_t **victims,
13796                                  svn_wc__db_t *db,
13797                                  const char *local_abspath,
13798                                  apr_pool_t *result_pool,
13799                                  apr_pool_t *scratch_pool)
13800 {
13801   svn_wc__db_wcroot_t *wcroot;
13802   const char *local_relpath;
13803   svn_sqlite__stmt_t *stmt;
13804   svn_boolean_t have_row;
13805   apr_array_header_t *new_victims;
13806
13807   /* The parent should be a working copy directory. */
13808   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13809                               local_abspath, scratch_pool, scratch_pool));
13810   VERIFY_USABLE_WCROOT(wcroot);
13811
13812   /* ### This will be much easier once we have all conflicts in one
13813          field of actual*/
13814
13815   /* Look for text, tree and property conflicts in ACTUAL */
13816   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13817                                     STMT_SELECT_CONFLICT_VICTIMS));
13818   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13819
13820   new_victims = apr_array_make(result_pool, 0, sizeof(const char *));
13821
13822   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13823   while (have_row)
13824     {
13825       const char *child_relpath = svn_sqlite__column_text(stmt, 0, NULL);
13826
13827       APR_ARRAY_PUSH(new_victims, const char *) =
13828                             svn_relpath_basename(child_relpath, result_pool);
13829
13830       SVN_ERR(svn_sqlite__step(&have_row, stmt));
13831     }
13832
13833   SVN_ERR(svn_sqlite__reset(stmt));
13834
13835   *victims = new_victims;
13836   return SVN_NO_ERROR;
13837 }
13838
13839 /* The body of svn_wc__db_get_conflict_marker_files().
13840  */
13841 static svn_error_t *
13842 get_conflict_marker_files(apr_hash_t **marker_files_p,
13843                           svn_wc__db_wcroot_t *wcroot,
13844                           const char *local_relpath,
13845                           svn_wc__db_t *db,
13846                           apr_pool_t *result_pool,
13847                           apr_pool_t *scratch_pool)
13848 {
13849   svn_sqlite__stmt_t *stmt;
13850   svn_boolean_t have_row;
13851   apr_hash_t *marker_files = apr_hash_make(result_pool);
13852
13853   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13854                                     STMT_SELECT_ACTUAL_NODE));
13855   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13856   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13857
13858   if (have_row && !svn_sqlite__column_is_null(stmt, 2))
13859     {
13860       apr_size_t len;
13861       const void *data = svn_sqlite__column_blob(stmt, 2, &len, NULL);
13862       svn_skel_t *conflicts;
13863       const apr_array_header_t *markers;
13864       int i;
13865
13866       conflicts = svn_skel__parse(data, len, scratch_pool);
13867
13868       /* ### ADD markers to *marker_files */
13869       SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13870                                             conflicts,
13871                                             result_pool, scratch_pool));
13872
13873       for (i = 0; markers && (i < markers->nelts); i++)
13874         {
13875           const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13876
13877           svn_hash_sets(marker_files, marker_abspath, "");
13878         }
13879     }
13880   SVN_ERR(svn_sqlite__reset(stmt));
13881
13882   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13883                                     STMT_SELECT_CONFLICT_VICTIMS));
13884   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13885   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13886
13887   while (have_row)
13888     {
13889       apr_size_t len;
13890       const void *data = svn_sqlite__column_blob(stmt, 1, &len, NULL);
13891
13892       const apr_array_header_t *markers;
13893       int i;
13894
13895       if (data)
13896         {
13897           svn_skel_t *conflicts;
13898           conflicts = svn_skel__parse(data, len, scratch_pool);
13899
13900           SVN_ERR(svn_wc__conflict_read_markers(&markers, db, wcroot->abspath,
13901                                                 conflicts,
13902                                                 result_pool, scratch_pool));
13903
13904           for (i = 0; markers && (i < markers->nelts); i++)
13905             {
13906               const char *marker_abspath = APR_ARRAY_IDX(markers, i, const char*);
13907
13908               svn_hash_sets(marker_files, marker_abspath, "");
13909             }
13910         }
13911
13912       SVN_ERR(svn_sqlite__step(&have_row, stmt));
13913     }
13914
13915   if (apr_hash_count(marker_files))
13916     *marker_files_p = marker_files;
13917   else
13918     *marker_files_p = NULL;
13919
13920   return svn_error_trace(svn_sqlite__reset(stmt));
13921 }
13922
13923 svn_error_t *
13924 svn_wc__db_get_conflict_marker_files(apr_hash_t **marker_files,
13925                                      svn_wc__db_t *db,
13926                                      const char *local_abspath,
13927                                      apr_pool_t *result_pool,
13928                                      apr_pool_t *scratch_pool)
13929 {
13930   svn_wc__db_wcroot_t *wcroot;
13931   const char *local_relpath;
13932
13933   /* The parent should be a working copy directory. */
13934   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13935                               local_abspath, scratch_pool, scratch_pool));
13936   VERIFY_USABLE_WCROOT(wcroot);
13937
13938   SVN_WC__DB_WITH_TXN(
13939     get_conflict_marker_files(marker_files, wcroot, local_relpath, db,
13940                               result_pool, scratch_pool),
13941     wcroot);
13942
13943   return SVN_NO_ERROR;
13944 }
13945
13946
13947 svn_error_t *
13948 svn_wc__db_read_conflict(svn_skel_t **conflict,
13949                          svn_node_kind_t *kind,
13950                          apr_hash_t **props,
13951                          svn_wc__db_t *db,
13952                          const char *local_abspath,
13953                          apr_pool_t *result_pool,
13954                          apr_pool_t *scratch_pool)
13955 {
13956   svn_wc__db_wcroot_t *wcroot;
13957   const char *local_relpath;
13958
13959   /* The parent should be a working copy directory. */
13960   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
13961                               local_abspath, scratch_pool, scratch_pool));
13962   VERIFY_USABLE_WCROOT(wcroot);
13963
13964   return svn_error_trace(svn_wc__db_read_conflict_internal(conflict, kind, props,
13965                                                            wcroot, local_relpath,
13966                                                            result_pool,
13967                                                            scratch_pool));
13968 }
13969
13970 svn_error_t *
13971 svn_wc__db_read_conflict_internal(svn_skel_t **conflict,
13972                                   svn_node_kind_t *kind,
13973                                   apr_hash_t **props,
13974                                   svn_wc__db_wcroot_t *wcroot,
13975                                   const char *local_relpath,
13976                                   apr_pool_t *result_pool,
13977                                   apr_pool_t *scratch_pool)
13978 {
13979   svn_sqlite__stmt_t *stmt;
13980   svn_boolean_t have_row;
13981
13982   if (kind)
13983     *kind = svn_node_none;
13984   if (props)
13985     *props = NULL;
13986
13987   /* Check if we have a conflict in ACTUAL */
13988   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
13989                                     STMT_SELECT_ACTUAL_NODE));
13990   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
13991
13992   SVN_ERR(svn_sqlite__step(&have_row, stmt));
13993
13994   if (have_row)
13995     {
13996       apr_size_t cfl_len;
13997       const void *cfl_data;
13998
13999       /* svn_skel__parse doesn't copy data, so store in result_pool */
14000       cfl_data = svn_sqlite__column_blob(stmt, 2, &cfl_len, result_pool);
14001
14002       if (cfl_data)
14003         *conflict = svn_skel__parse(cfl_data, cfl_len, result_pool);
14004       else
14005         *conflict = NULL;
14006
14007       if (props)
14008         {
14009           svn_error_t *err;
14010
14011           err = svn_error_trace(svn_sqlite__column_properties(props, stmt, 1,
14012                                                               result_pool,
14013                                                               scratch_pool));
14014
14015           if (err)
14016             return svn_error_compose_create(err, svn_sqlite__reset(stmt));
14017         }
14018     }
14019   else
14020     *conflict = NULL;
14021
14022   SVN_ERR(svn_sqlite__reset(stmt));
14023
14024   if (!have_row || kind || (props && !*props))
14025     {
14026       svn_error_t *err = NULL;
14027       svn_boolean_t have_info = FALSE;
14028
14029       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14030                                         STMT_SELECT_NODE_INFO));
14031
14032       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id,
14033                                 local_relpath));
14034
14035       SVN_ERR(svn_sqlite__step(&have_info, stmt));
14036
14037       if (have_info)
14038         {
14039           if (kind)
14040             {
14041               svn_wc__db_status_t status;
14042               int op_depth = svn_sqlite__column_int(stmt, 0);
14043
14044               status = svn_sqlite__column_token(stmt, 3, presence_map);
14045
14046               if (op_depth > 0)
14047                 err = convert_to_working_status(&status, status);
14048
14049               if (!err && (status == svn_wc__db_status_normal
14050                            || status == svn_wc__db_status_added
14051                            || status == svn_wc__db_status_deleted
14052                            || status == svn_wc__db_status_incomplete))
14053                 {
14054                   *kind = svn_sqlite__column_token(stmt, 4, kind_map);
14055                 }
14056             }
14057
14058           /* Need props, and no props in ACTUAL? */
14059           if (!err && (props && !*props))
14060             {
14061               err = svn_sqlite__column_properties(props, stmt, 14,
14062                                                   result_pool, scratch_pool);
14063             }
14064         }
14065
14066       SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14067
14068       if (!have_row && !have_info)
14069         {
14070           return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14071                                    _("The node '%s' was not found."),
14072                                    path_for_error_message(wcroot,
14073                                                           local_relpath,
14074                                                           scratch_pool));
14075         }
14076     }
14077
14078   return SVN_NO_ERROR;
14079 }
14080
14081
14082 svn_error_t *
14083 svn_wc__db_read_kind(svn_node_kind_t *kind,
14084                      svn_wc__db_t *db,
14085                      const char *local_abspath,
14086                      svn_boolean_t allow_missing,
14087                      svn_boolean_t show_deleted,
14088                      svn_boolean_t show_hidden,
14089                      apr_pool_t *scratch_pool)
14090 {
14091   svn_wc__db_wcroot_t *wcroot;
14092   const char *local_relpath;
14093   svn_sqlite__stmt_t *stmt_info;
14094   svn_boolean_t have_info;
14095   svn_wc__db_status_t status;
14096
14097   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14098
14099   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14100                               local_abspath, scratch_pool, scratch_pool));
14101   VERIFY_USABLE_WCROOT(wcroot);
14102
14103   SVN_ERR(svn_sqlite__get_statement(&stmt_info, wcroot->sdb,
14104                                     STMT_SELECT_NODE_INFO));
14105   SVN_ERR(svn_sqlite__bindf(stmt_info, "is", wcroot->wc_id, local_relpath));
14106   SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
14107
14108   if (!have_info)
14109     {
14110       if (allow_missing)
14111         {
14112           *kind = svn_node_unknown;
14113           SVN_ERR(svn_sqlite__reset(stmt_info));
14114           return SVN_NO_ERROR;
14115         }
14116       else
14117         {
14118           SVN_ERR(svn_sqlite__reset(stmt_info));
14119           return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14120                                    _("The node '%s' was not found."),
14121                                    path_for_error_message(wcroot,
14122                                                           local_relpath,
14123                                                           scratch_pool));
14124         }
14125     }
14126
14127   status = svn_sqlite__column_token(stmt_info, 3, presence_map);
14128
14129   if (show_deleted && status == svn_wc__db_status_base_deleted)
14130     {
14131       /* Let's return the kind of what is really deleted insead of what
14132          we have cached in the base-deleted record */
14133
14134       SVN_ERR(svn_sqlite__step(&have_info, stmt_info));
14135
14136       if (!have_info)
14137         {
14138           /* No lower layer deleted? Database inconsistency! */
14139           *kind = svn_node_none;
14140           return svn_error_trace(svn_sqlite__reset(stmt_info));
14141         }
14142     }
14143
14144   if (!(show_deleted && show_hidden))
14145     {
14146       int op_depth = svn_sqlite__column_int(stmt_info, 0);
14147       svn_boolean_t report_none = FALSE;
14148
14149       if (op_depth > 0)
14150         SVN_ERR(convert_to_working_status(&status, status));
14151
14152       switch (status)
14153         {
14154           case svn_wc__db_status_not_present:
14155             if (! (show_hidden && show_deleted))
14156               report_none = TRUE;
14157             break;
14158           case svn_wc__db_status_excluded:
14159           case svn_wc__db_status_server_excluded:
14160             if (! show_hidden)
14161               report_none = TRUE;
14162             break;
14163           case svn_wc__db_status_deleted:
14164             if (! show_deleted)
14165               report_none = TRUE;
14166             break;
14167           default:
14168             break;
14169         }
14170
14171       if (report_none)
14172         {
14173           *kind = svn_node_none;
14174           return svn_error_trace(svn_sqlite__reset(stmt_info));
14175         }
14176     }
14177
14178   *kind = svn_sqlite__column_token(stmt_info, 4, kind_map);
14179
14180   return svn_error_trace(svn_sqlite__reset(stmt_info));
14181 }
14182
14183 svn_error_t *
14184 svn_wc__db_is_wcroot(svn_boolean_t *is_wcroot,
14185                      svn_wc__db_t *db,
14186                      const char *local_abspath,
14187                      apr_pool_t *scratch_pool)
14188 {
14189   svn_wc__db_wcroot_t *wcroot;
14190   const char *local_relpath;
14191
14192   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14193
14194   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14195                               local_abspath, scratch_pool, scratch_pool));
14196   VERIFY_USABLE_WCROOT(wcroot);
14197
14198   if (*local_relpath != '\0')
14199     {
14200       *is_wcroot = FALSE; /* Node is a file, or has a parent directory within
14201                            the same wcroot */
14202       return SVN_NO_ERROR;
14203     }
14204
14205    *is_wcroot = TRUE;
14206
14207    return SVN_NO_ERROR;
14208 }
14209
14210 /* Find a node's kind and whether it is switched, putting the outputs in
14211  * *IS_SWITCHED and *KIND. Either of the outputs may be NULL if not wanted.
14212  */
14213 static svn_error_t *
14214 db_is_switched(svn_boolean_t *is_switched,
14215                svn_node_kind_t *kind,
14216                svn_wc__db_wcroot_t *wcroot,
14217                const char *local_relpath,
14218                apr_pool_t *scratch_pool)
14219 {
14220   svn_wc__db_status_t status;
14221   apr_int64_t repos_id;
14222   const char *repos_relpath;
14223   const char *name;
14224   const char *parent_local_relpath;
14225   apr_int64_t parent_repos_id;
14226   const char *parent_repos_relpath;
14227
14228   SVN_ERR_ASSERT(*local_relpath != '\0'); /* Handled in wrapper */
14229
14230   SVN_ERR(read_info(&status, kind, NULL, &repos_relpath, &repos_id, NULL,
14231                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
14232                     NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
14233                     wcroot, local_relpath, scratch_pool, scratch_pool));
14234
14235   if (status == svn_wc__db_status_server_excluded
14236       || status == svn_wc__db_status_excluded
14237       || status == svn_wc__db_status_not_present)
14238     {
14239       return svn_error_createf(
14240                     SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14241                     _("The node '%s' was not found."),
14242                     path_for_error_message(wcroot, local_relpath,
14243                                            scratch_pool));
14244     }
14245   else if (! repos_relpath)
14246     {
14247       /* Node is shadowed; easy out */
14248       if (is_switched)
14249         *is_switched = FALSE;
14250
14251       return SVN_NO_ERROR;
14252     }
14253
14254   if (! is_switched)
14255     return SVN_NO_ERROR;
14256
14257   svn_relpath_split(&parent_local_relpath, &name, local_relpath, scratch_pool);
14258
14259   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
14260                                             &parent_repos_relpath,
14261                                             &parent_repos_id, NULL, NULL, NULL,
14262                                             NULL, NULL, NULL, NULL, NULL,
14263                                             NULL, NULL,
14264                                             wcroot, parent_local_relpath,
14265                                             scratch_pool, scratch_pool));
14266
14267   if (repos_id != parent_repos_id)
14268     *is_switched = TRUE;
14269   else
14270     {
14271       const char *expected_relpath;
14272
14273       expected_relpath = svn_relpath_join(parent_repos_relpath, name,
14274                                           scratch_pool);
14275
14276       *is_switched = (strcmp(expected_relpath, repos_relpath) != 0);
14277     }
14278
14279   return SVN_NO_ERROR;
14280 }
14281
14282 svn_error_t *
14283 svn_wc__db_is_switched(svn_boolean_t *is_wcroot,
14284                        svn_boolean_t *is_switched,
14285                        svn_node_kind_t *kind,
14286                        svn_wc__db_t *db,
14287                        const char *local_abspath,
14288                        apr_pool_t *scratch_pool)
14289 {
14290   svn_wc__db_wcroot_t *wcroot;
14291   const char *local_relpath;
14292
14293   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14294
14295   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14296                               local_abspath, scratch_pool, scratch_pool));
14297   VERIFY_USABLE_WCROOT(wcroot);
14298
14299   if (is_switched)
14300     *is_switched = FALSE;
14301
14302   if (*local_relpath == '\0')
14303     {
14304       /* Easy out */
14305       if (is_wcroot)
14306         *is_wcroot = TRUE;
14307
14308       if (kind)
14309         *kind = svn_node_dir;
14310       return SVN_NO_ERROR;
14311     }
14312
14313   if (is_wcroot)
14314     *is_wcroot = FALSE;
14315
14316   if (! is_switched && ! kind)
14317     return SVN_NO_ERROR;
14318
14319   SVN_WC__DB_WITH_TXN(
14320     db_is_switched(is_switched, kind, wcroot, local_relpath, scratch_pool),
14321     wcroot);
14322   return SVN_NO_ERROR;
14323 }
14324
14325
14326 svn_error_t *
14327 svn_wc__db_temp_wcroot_tempdir(const char **temp_dir_abspath,
14328                                svn_wc__db_t *db,
14329                                const char *wri_abspath,
14330                                apr_pool_t *result_pool,
14331                                apr_pool_t *scratch_pool)
14332 {
14333   svn_wc__db_wcroot_t *wcroot;
14334   const char *local_relpath;
14335
14336   SVN_ERR_ASSERT(temp_dir_abspath != NULL);
14337   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
14338
14339   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14340                               wri_abspath, scratch_pool, scratch_pool));
14341   VERIFY_USABLE_WCROOT(wcroot);
14342
14343   *temp_dir_abspath = svn_dirent_join_many(result_pool,
14344                                            wcroot->abspath,
14345                                            svn_wc_get_adm_dir(scratch_pool),
14346                                            WCROOT_TEMPDIR_RELPATH,
14347                                            SVN_VA_NULL);
14348   return SVN_NO_ERROR;
14349 }
14350
14351
14352 /* Helper for wclock_obtain_cb() to steal an existing lock */
14353 static svn_error_t *
14354 wclock_steal(svn_wc__db_wcroot_t *wcroot,
14355              const char *local_relpath,
14356              apr_pool_t *scratch_pool)
14357 {
14358   svn_sqlite__stmt_t *stmt;
14359
14360   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_DELETE_WC_LOCK));
14361   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14362
14363   SVN_ERR(svn_sqlite__step_done(stmt));
14364
14365   return SVN_NO_ERROR;
14366 }
14367
14368
14369 /* The body of svn_wc__db_wclock_obtain().
14370  */
14371 static svn_error_t *
14372 wclock_obtain_cb(svn_wc__db_wcroot_t *wcroot,
14373                  const char *local_relpath,
14374                  int levels_to_lock,
14375                  svn_boolean_t steal_lock,
14376                  svn_boolean_t enforce_empty_wq,
14377                  apr_pool_t *scratch_pool)
14378 {
14379   svn_sqlite__stmt_t *stmt;
14380   svn_error_t *err;
14381   const char *lock_relpath;
14382   int max_depth;
14383   int lock_depth;
14384   svn_boolean_t got_row;
14385
14386   svn_wc__db_wclock_t lock;
14387
14388   /* Upgrade locks the root before the node exists.  Apart from that
14389      the root node always exists so we will just skip the check.
14390
14391      ### Perhaps the lock for upgrade should be created when the db is
14392          created?  1.6 used to lock .svn on creation. */
14393   if (local_relpath[0])
14394     {
14395       svn_boolean_t exists;
14396
14397       SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
14398       if (!exists)
14399         return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
14400                                  _("The node '%s' was not found."),
14401                                  path_for_error_message(wcroot,
14402                                                         local_relpath,
14403                                                         scratch_pool));
14404     }
14405
14406   if (enforce_empty_wq)
14407     SVN_ERR(svn_wc__db_verify_no_work(wcroot->sdb));
14408
14409   /* Check if there are nodes locked below the new lock root */
14410   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_FIND_WC_LOCK));
14411   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14412
14413   lock_depth = relpath_depth(local_relpath);
14414   max_depth = lock_depth + levels_to_lock;
14415
14416   SVN_ERR(svn_sqlite__step(&got_row, stmt));
14417
14418   while (got_row)
14419     {
14420       svn_boolean_t own_lock;
14421
14422       lock_relpath = svn_sqlite__column_text(stmt, 0, scratch_pool);
14423
14424       /* If we are not locking with depth infinity, check if this lock
14425          voids our lock request */
14426       if (levels_to_lock >= 0
14427           && relpath_depth(lock_relpath) > max_depth)
14428         {
14429           SVN_ERR(svn_sqlite__step(&got_row, stmt));
14430           continue;
14431         }
14432
14433       /* Check if we are the lock owner, because we should be able to
14434          extend our lock. */
14435       err = svn_wc__db_wclock_owns_lock_internal(&own_lock, wcroot,
14436                                                  lock_relpath,
14437                                                  TRUE, scratch_pool);
14438
14439       if (err)
14440         SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14441
14442       if (!own_lock && !steal_lock)
14443         {
14444           SVN_ERR(svn_sqlite__reset(stmt));
14445           err = svn_error_createf(SVN_ERR_WC_LOCKED, NULL,
14446                                    _("'%s' is already locked."),
14447                                    path_for_error_message(wcroot,
14448                                                           lock_relpath,
14449                                                           scratch_pool));
14450           return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14451                                    _("Working copy '%s' locked."),
14452                                    path_for_error_message(wcroot,
14453                                                           local_relpath,
14454                                                           scratch_pool));
14455         }
14456       else if (!own_lock)
14457         {
14458           err = wclock_steal(wcroot, lock_relpath, scratch_pool);
14459
14460           if (err)
14461             SVN_ERR(svn_error_compose_create(err, svn_sqlite__reset(stmt)));
14462         }
14463
14464       SVN_ERR(svn_sqlite__step(&got_row, stmt));
14465     }
14466
14467   SVN_ERR(svn_sqlite__reset(stmt));
14468
14469   if (steal_lock)
14470     SVN_ERR(wclock_steal(wcroot, local_relpath, scratch_pool));
14471
14472   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_SELECT_WC_LOCK));
14473   lock_relpath = local_relpath;
14474
14475   while (TRUE)
14476     {
14477       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, lock_relpath));
14478
14479       SVN_ERR(svn_sqlite__step(&got_row, stmt));
14480
14481       if (got_row)
14482         {
14483           int levels = svn_sqlite__column_int(stmt, 0);
14484           if (levels >= 0)
14485             levels += relpath_depth(lock_relpath);
14486
14487           SVN_ERR(svn_sqlite__reset(stmt));
14488
14489           if (levels == -1 || levels >= lock_depth)
14490             {
14491
14492               err = svn_error_createf(
14493                               SVN_ERR_WC_LOCKED, NULL,
14494                               _("'%s' is already locked."),
14495                               svn_dirent_local_style(
14496                                        svn_dirent_join(wcroot->abspath,
14497                                                        lock_relpath,
14498                                                        scratch_pool),
14499                               scratch_pool));
14500               return svn_error_createf(
14501                               SVN_ERR_WC_LOCKED, err,
14502                               _("Working copy '%s' locked."),
14503                               path_for_error_message(wcroot,
14504                                                      local_relpath,
14505                                                      scratch_pool));
14506             }
14507
14508           break; /* There can't be interesting locks on higher nodes */
14509         }
14510       else
14511         SVN_ERR(svn_sqlite__reset(stmt));
14512
14513       if (!*lock_relpath)
14514         break;
14515
14516       lock_relpath = svn_relpath_dirname(lock_relpath, scratch_pool);
14517     }
14518
14519   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_INSERT_WC_LOCK));
14520   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
14521                             levels_to_lock));
14522   err = svn_sqlite__insert(NULL, stmt);
14523   if (err)
14524     return svn_error_createf(SVN_ERR_WC_LOCKED, err,
14525                              _("Failed to lock working copy '%s'."),
14526                              path_for_error_message(wcroot,
14527                                                     local_relpath,
14528                                                     scratch_pool));
14529
14530   /* And finally store that we obtained the lock */
14531   lock.local_relpath = apr_pstrdup(wcroot->owned_locks->pool, local_relpath);
14532   lock.levels = levels_to_lock;
14533   APR_ARRAY_PUSH(wcroot->owned_locks, svn_wc__db_wclock_t) = lock;
14534
14535   return SVN_NO_ERROR;
14536 }
14537
14538
14539 svn_error_t *
14540 svn_wc__db_wclock_obtain(svn_wc__db_t *db,
14541                          const char *local_abspath,
14542                          int levels_to_lock,
14543                          svn_boolean_t steal_lock,
14544                          apr_pool_t *scratch_pool)
14545 {
14546   svn_wc__db_wcroot_t *wcroot;
14547   const char *local_relpath;
14548
14549   SVN_ERR_ASSERT(levels_to_lock >= -1);
14550   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14551
14552   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
14553                                              db, local_abspath,
14554                                              scratch_pool, scratch_pool));
14555   VERIFY_USABLE_WCROOT(wcroot);
14556
14557   if (!steal_lock)
14558     {
14559       int i;
14560       int depth = relpath_depth(local_relpath);
14561
14562       for (i = 0; i < wcroot->owned_locks->nelts; i++)
14563         {
14564           svn_wc__db_wclock_t* lock = &APR_ARRAY_IDX(wcroot->owned_locks,
14565                                                      i, svn_wc__db_wclock_t);
14566
14567           if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14568               && (lock->levels == -1
14569                   || (lock->levels + relpath_depth(lock->local_relpath))
14570                             >= depth))
14571             {
14572               return svn_error_createf(
14573                 SVN_ERR_WC_LOCKED, NULL,
14574                 _("'%s' is already locked via '%s'."),
14575                 svn_dirent_local_style(local_abspath, scratch_pool),
14576                 path_for_error_message(wcroot, lock->local_relpath,
14577                                        scratch_pool));
14578             }
14579         }
14580     }
14581
14582   SVN_WC__DB_WITH_TXN(
14583     wclock_obtain_cb(wcroot, local_relpath, levels_to_lock, steal_lock,
14584                      db->enforce_empty_wq, scratch_pool),
14585     wcroot);
14586   return SVN_NO_ERROR;
14587 }
14588
14589
14590 /* The body of svn_wc__db_wclock_find_root() and svn_wc__db_wclocked(). */
14591 static svn_error_t *
14592 find_wclock(const char **lock_relpath,
14593             svn_wc__db_wcroot_t *wcroot,
14594             const char *dir_relpath,
14595             apr_pool_t *result_pool,
14596             apr_pool_t *scratch_pool)
14597 {
14598   svn_sqlite__stmt_t *stmt;
14599   svn_boolean_t have_row;
14600   int dir_depth = relpath_depth(dir_relpath);
14601   const char *first_relpath;
14602
14603   /* Check for locks on all directories that might be ancestors.
14604      As our new apis only use recursive locks the number of locks stored
14605      in the DB will be very low */
14606   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14607                                     STMT_SELECT_ANCESTOR_WCLOCKS));
14608
14609   /* Get the top level relpath to reduce the worst case number of results
14610      to the number of directories below this node plus two.
14611      (1: the node itself and 2: the wcroot). */
14612   first_relpath = strchr(dir_relpath, '/');
14613
14614   if (first_relpath != NULL)
14615     first_relpath = apr_pstrndup(scratch_pool, dir_relpath,
14616                                  first_relpath - dir_relpath);
14617   else
14618     first_relpath = dir_relpath;
14619
14620   SVN_ERR(svn_sqlite__bindf(stmt, "iss",
14621                             wcroot->wc_id,
14622                             dir_relpath,
14623                             first_relpath));
14624
14625   SVN_ERR(svn_sqlite__step(&have_row, stmt));
14626
14627   while (have_row)
14628     {
14629       const char *relpath = svn_sqlite__column_text(stmt, 0, NULL);
14630
14631       if (svn_relpath_skip_ancestor(relpath, dir_relpath))
14632         {
14633           int locked_levels = svn_sqlite__column_int(stmt, 1);
14634           int row_depth = relpath_depth(relpath);
14635
14636           if (locked_levels == -1
14637               || locked_levels + row_depth >= dir_depth)
14638             {
14639               *lock_relpath = apr_pstrdup(result_pool, relpath);
14640               SVN_ERR(svn_sqlite__reset(stmt));
14641               return SVN_NO_ERROR;
14642             }
14643         }
14644
14645       SVN_ERR(svn_sqlite__step(&have_row, stmt));
14646     }
14647
14648   *lock_relpath = NULL;
14649
14650   return svn_error_trace(svn_sqlite__reset(stmt));
14651 }
14652
14653 static svn_error_t *
14654 is_wclocked(svn_boolean_t *locked,
14655             svn_wc__db_wcroot_t *wcroot,
14656             const char *dir_relpath,
14657             apr_pool_t *scratch_pool)
14658 {
14659   const char *lock_relpath;
14660
14661   SVN_ERR(find_wclock(&lock_relpath, wcroot, dir_relpath,
14662                       scratch_pool, scratch_pool));
14663   *locked = (lock_relpath != NULL);
14664   return SVN_NO_ERROR;
14665 }
14666
14667
14668 svn_error_t*
14669 svn_wc__db_wclock_find_root(const char **lock_abspath,
14670                             svn_wc__db_t *db,
14671                             const char *local_abspath,
14672                             apr_pool_t *result_pool,
14673                             apr_pool_t *scratch_pool)
14674 {
14675   svn_wc__db_wcroot_t *wcroot;
14676   const char *local_relpath;
14677   const char *lock_relpath;
14678
14679   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14680                               local_abspath, scratch_pool, scratch_pool));
14681   VERIFY_USABLE_WCROOT(wcroot);
14682
14683   SVN_WC__DB_WITH_TXN(
14684     find_wclock(&lock_relpath, wcroot, local_relpath,
14685                 scratch_pool, scratch_pool),
14686     wcroot);
14687
14688   if (!lock_relpath)
14689     *lock_abspath = NULL;
14690   else
14691     SVN_ERR(svn_wc__db_from_relpath(lock_abspath, db, wcroot->abspath,
14692                                     lock_relpath, result_pool, scratch_pool));
14693   return SVN_NO_ERROR;
14694 }
14695
14696
14697 svn_error_t *
14698 svn_wc__db_wclocked(svn_boolean_t *locked,
14699                     svn_wc__db_t *db,
14700                     const char *local_abspath,
14701                     apr_pool_t *scratch_pool)
14702 {
14703   svn_wc__db_wcroot_t *wcroot;
14704   const char *local_relpath;
14705
14706   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14707                               local_abspath, scratch_pool, scratch_pool));
14708   VERIFY_USABLE_WCROOT(wcroot);
14709
14710   SVN_WC__DB_WITH_TXN(
14711     is_wclocked(locked, wcroot, local_relpath, scratch_pool),
14712     wcroot);
14713
14714   return SVN_NO_ERROR;
14715 }
14716
14717
14718 svn_error_t *
14719 svn_wc__db_wclock_release(svn_wc__db_t *db,
14720                           const char *local_abspath,
14721                           apr_pool_t *scratch_pool)
14722 {
14723   svn_sqlite__stmt_t *stmt;
14724   svn_wc__db_wcroot_t *wcroot;
14725   const char *local_relpath;
14726   int i;
14727   apr_array_header_t *owned_locks;
14728
14729   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14730                               local_abspath, scratch_pool, scratch_pool));
14731
14732   VERIFY_USABLE_WCROOT(wcroot);
14733
14734   /* First check and remove the owns-lock information as failure in
14735      removing the db record implies that we have to steal the lock later. */
14736   owned_locks = wcroot->owned_locks;
14737   for (i = 0; i < owned_locks->nelts; i++)
14738     {
14739       svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14740                                                  svn_wc__db_wclock_t);
14741
14742       if (strcmp(lock->local_relpath, local_relpath) == 0)
14743         break;
14744     }
14745
14746   if (i >= owned_locks->nelts)
14747     return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
14748                              _("Working copy not locked at '%s'."),
14749                              svn_dirent_local_style(local_abspath,
14750                                                     scratch_pool));
14751
14752   if (i < owned_locks->nelts)
14753     {
14754       owned_locks->nelts--;
14755
14756       /* Move the last item in the array to the deleted place */
14757       if (owned_locks->nelts > 0)
14758         APR_ARRAY_IDX(owned_locks, i, svn_wc__db_wclock_t) =
14759            APR_ARRAY_IDX(owned_locks, owned_locks->nelts, svn_wc__db_wclock_t);
14760     }
14761
14762   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14763                                     STMT_DELETE_WC_LOCK));
14764
14765   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
14766
14767   SVN_ERR(svn_sqlite__step_done(stmt));
14768
14769   return SVN_NO_ERROR;
14770 }
14771
14772
14773 /* Like svn_wc__db_wclock_owns_lock() but taking WCROOT+LOCAL_RELPATH instead
14774    of DB+LOCAL_ABSPATH.  */
14775 svn_error_t *
14776 svn_wc__db_wclock_owns_lock_internal(svn_boolean_t *own_lock,
14777                                      svn_wc__db_wcroot_t *wcroot,
14778                                      const char *local_relpath,
14779                                      svn_boolean_t exact,
14780                                      apr_pool_t *scratch_pool)
14781 {
14782   apr_array_header_t *owned_locks;
14783   int lock_level;
14784   int i;
14785
14786   *own_lock = FALSE;
14787   owned_locks = wcroot->owned_locks;
14788   lock_level = relpath_depth(local_relpath);
14789
14790   if (exact)
14791     {
14792       for (i = 0; i < owned_locks->nelts; i++)
14793         {
14794           svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14795                                                      svn_wc__db_wclock_t);
14796
14797           if (strcmp(lock->local_relpath, local_relpath) == 0)
14798             {
14799               *own_lock = TRUE;
14800               return SVN_NO_ERROR;
14801             }
14802         }
14803     }
14804   else
14805     {
14806       for (i = 0; i < owned_locks->nelts; i++)
14807         {
14808           svn_wc__db_wclock_t *lock = &APR_ARRAY_IDX(owned_locks, i,
14809                                                      svn_wc__db_wclock_t);
14810
14811           if (svn_relpath_skip_ancestor(lock->local_relpath, local_relpath)
14812               && (lock->levels == -1
14813                   || ((relpath_depth(lock->local_relpath) + lock->levels)
14814                       >= lock_level)))
14815             {
14816               *own_lock = TRUE;
14817               return SVN_NO_ERROR;
14818             }
14819         }
14820     }
14821
14822   return SVN_NO_ERROR;
14823 }
14824
14825
14826 svn_error_t *
14827 svn_wc__db_wclock_owns_lock(svn_boolean_t *own_lock,
14828                             svn_wc__db_t *db,
14829                             const char *local_abspath,
14830                             svn_boolean_t exact,
14831                             apr_pool_t *scratch_pool)
14832 {
14833   svn_wc__db_wcroot_t *wcroot;
14834   const char *local_relpath;
14835
14836   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14837                               local_abspath, scratch_pool, scratch_pool));
14838
14839   if (!wcroot)
14840     return svn_error_createf(SVN_ERR_WC_NOT_WORKING_COPY, NULL,
14841                              _("The node '%s' was not found."),
14842                              svn_dirent_local_style(local_abspath,
14843                                                     scratch_pool));
14844
14845   VERIFY_USABLE_WCROOT(wcroot);
14846
14847   SVN_ERR(svn_wc__db_wclock_owns_lock_internal(own_lock, wcroot, local_relpath,
14848                                                exact, scratch_pool));
14849
14850   return SVN_NO_ERROR;
14851 }
14852
14853 /* The body of svn_wc__db_temp_op_end_directory_update().
14854  */
14855 static svn_error_t *
14856 end_directory_update(svn_wc__db_wcroot_t *wcroot,
14857                      const char *local_relpath,
14858                      apr_pool_t *scratch_pool)
14859 {
14860   svn_sqlite__stmt_t *stmt;
14861   svn_wc__db_status_t base_status;
14862
14863   SVN_ERR(svn_wc__db_base_get_info_internal(&base_status, NULL, NULL, NULL,
14864                                             NULL, NULL, NULL, NULL, NULL,
14865                                             NULL, NULL, NULL, NULL, NULL, NULL,
14866                                             wcroot, local_relpath,
14867                                             scratch_pool, scratch_pool));
14868
14869   if (base_status == svn_wc__db_status_normal)
14870     return SVN_NO_ERROR;
14871
14872   SVN_ERR_ASSERT(base_status == svn_wc__db_status_incomplete);
14873
14874   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14875                                     STMT_UPDATE_NODE_BASE_PRESENCE));
14876   SVN_ERR(svn_sqlite__bindf(stmt, "ist", wcroot->wc_id, local_relpath,
14877                             presence_map, svn_wc__db_status_normal));
14878   SVN_ERR(svn_sqlite__step_done(stmt));
14879
14880   return SVN_NO_ERROR;
14881 }
14882
14883 svn_error_t *
14884 svn_wc__db_temp_op_end_directory_update(svn_wc__db_t *db,
14885                                         const char *local_dir_abspath,
14886                                         apr_pool_t *scratch_pool)
14887 {
14888   svn_wc__db_wcroot_t *wcroot;
14889   const char *local_relpath;
14890
14891   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_dir_abspath));
14892
14893   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14894                               local_dir_abspath, scratch_pool, scratch_pool));
14895   VERIFY_USABLE_WCROOT(wcroot);
14896
14897   SVN_WC__DB_WITH_TXN(
14898     end_directory_update(wcroot, local_relpath, scratch_pool),
14899     wcroot);
14900
14901   SVN_ERR(flush_entries(wcroot, local_dir_abspath, svn_depth_empty,
14902                         scratch_pool));
14903
14904   return SVN_NO_ERROR;
14905 }
14906
14907
14908 /* The body of svn_wc__db_temp_op_start_directory_update().
14909  */
14910 static svn_error_t *
14911 start_directory_update_txn(svn_wc__db_wcroot_t *wcroot,
14912                            const char *local_relpath,
14913                            const char *new_repos_relpath,
14914                            svn_revnum_t new_rev,
14915                            apr_pool_t *scratch_pool)
14916 {
14917   svn_sqlite__stmt_t *stmt;
14918
14919   /* Note: In the majority of calls, the repos_relpath is unchanged. */
14920   /* ### TODO: Maybe check if we can make repos_relpath NULL. */
14921   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14922                     STMT_UPDATE_BASE_NODE_PRESENCE_REVNUM_AND_REPOS_PATH));
14923
14924   SVN_ERR(svn_sqlite__bindf(stmt, "istrs",
14925                             wcroot->wc_id,
14926                             local_relpath,
14927                             presence_map, svn_wc__db_status_incomplete,
14928                             new_rev,
14929                             new_repos_relpath));
14930   SVN_ERR(svn_sqlite__step_done(stmt));
14931
14932   return SVN_NO_ERROR;
14933
14934 }
14935
14936 svn_error_t *
14937 svn_wc__db_temp_op_start_directory_update(svn_wc__db_t *db,
14938                                           const char *local_abspath,
14939                                           const char *new_repos_relpath,
14940                                           svn_revnum_t new_rev,
14941                                           apr_pool_t *scratch_pool)
14942 {
14943   svn_wc__db_wcroot_t *wcroot;
14944   const char *local_relpath;
14945
14946   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
14947   SVN_ERR_ASSERT(SVN_IS_VALID_REVNUM(new_rev));
14948   SVN_ERR_ASSERT(svn_relpath_is_canonical(new_repos_relpath));
14949
14950   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
14951                               local_abspath, scratch_pool, scratch_pool));
14952   VERIFY_USABLE_WCROOT(wcroot);
14953
14954   SVN_WC__DB_WITH_TXN(
14955     start_directory_update_txn(wcroot, local_relpath,
14956                                new_repos_relpath, new_rev, scratch_pool),
14957     wcroot);
14958
14959   SVN_ERR(flush_entries(wcroot, local_abspath, svn_depth_empty, scratch_pool));
14960
14961   return SVN_NO_ERROR;
14962 }
14963
14964 /* Helper for svn_wc__db_op_make_copy_internal */
14965 static svn_error_t *
14966 db_move_moved_to(svn_wc__db_wcroot_t *wcroot,
14967                  const char *src1_relpath,
14968                  int src1_op_depth,
14969                  const char *src2_relpath,
14970                  int src2_op_depth,
14971                  const char *dst_relpath,
14972                  apr_pool_t *scratch_pool)
14973 {
14974   svn_sqlite__stmt_t *stmt;
14975   int affected_rows;
14976
14977   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14978                                      STMT_UPDATE_MOVED_TO_RELPATH));
14979   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id,
14980                             src1_relpath, src1_op_depth));
14981   SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
14982
14983   if (affected_rows == 1)
14984     {
14985       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
14986                                      STMT_UPDATE_MOVED_TO_RELPATH));
14987       SVN_ERR(svn_sqlite__bindf(stmt, "isds", wcroot->wc_id,
14988                                 src2_relpath, src2_op_depth,
14989                                 dst_relpath));
14990       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
14991     }
14992   if (affected_rows != 1)
14993     return svn_error_create(SVN_ERR_WC_PATH_NOT_FOUND, NULL, NULL);
14994
14995   return SVN_NO_ERROR;
14996 }
14997
14998 static svn_error_t *
14999 db_move_moved_to_down_recursive(svn_wc__db_wcroot_t *wcroot,
15000                                 const char *local_relpath,
15001                                 int new_shadow_layer,
15002                                 apr_pool_t *scratch_pool)
15003 {
15004   svn_sqlite__stmt_t *stmt;
15005   svn_boolean_t have_row;
15006   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15007
15008   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15009                         STMT_SELECT_MOVED_DESCENDANTS_SRC));
15010   SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15011                             new_shadow_layer));
15012   SVN_ERR(svn_sqlite__step(&have_row, stmt));
15013
15014   while (have_row)
15015     {
15016       int del_op_depth;
15017       const char *src_relpath;
15018       const char *dst_relpath;
15019       svn_error_t *err;
15020
15021       svn_pool_clear(iterpool);
15022
15023       del_op_depth = svn_sqlite__column_int(stmt, 0);
15024       src_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
15025       dst_relpath = svn_sqlite__column_text(stmt, 4, iterpool);
15026
15027       err = svn_error_trace(
15028                db_move_moved_to(
15029                              wcroot,
15030                              src_relpath, del_op_depth,
15031                              src_relpath, new_shadow_layer,
15032                              dst_relpath, iterpool));
15033
15034       if (err)
15035         return svn_error_compose_create(err, svn_sqlite__reset(stmt));
15036
15037       SVN_ERR(svn_sqlite__step(&have_row, stmt));
15038     }
15039
15040   SVN_ERR(svn_sqlite__reset(stmt));
15041
15042   return SVN_NO_ERROR;
15043 }
15044
15045
15046 /* The body of svn_wc__db_temp_op_make_copy().  This is
15047    used by the update editor when deleting a base node tree would be a
15048    tree-conflict because there are changes to subtrees.  This function
15049    inserts a copy of the base node tree below any existing working
15050    subtrees.  Given a tree:
15051
15052              0            1           2            3
15053     /     normal          -
15054     A     normal          -
15055     A/B   normal          -         normal
15056     A/B/C normal          -         base-del       normal
15057     A/F   normal          -         normal
15058     A/F/G normal          -         normal
15059     A/F/H normal          -         base-deleted   normal
15060     A/F/E normal          -         not-present
15061     A/X   normal          -
15062     A/X/Y incomplete      -
15063
15064     This function adds layers to A and some of its descendants in an attempt
15065     to make the working copy look like as if it were a copy of the BASE nodes.
15066
15067              0            1              2            3
15068     /     normal        -
15069     A     normal        norm
15070     A/B   normal        norm        norm
15071     A/B/C normal        norm        base-del       normal
15072     A/F   normal        norm        norm
15073     A/F/G normal        norm        norm
15074     A/F/H normal        norm        not-pres
15075     A/F/E normal        norm        base-del
15076     A/X   normal        norm
15077     A/X/Y incomplete  incomplete
15078  */
15079 static svn_error_t *
15080 make_copy_txn(svn_wc__db_wcroot_t *wcroot,
15081               const char *local_relpath,
15082               apr_int64_t last_repos_id,
15083               const char *last_repos_relpath,
15084               svn_revnum_t last_revision,
15085               int last_op_depth,
15086               svn_boolean_t shadowed,
15087               int root_shadow_depth,
15088               apr_pool_t *scratch_pool)
15089 {
15090   svn_sqlite__stmt_t *stmt;
15091   svn_boolean_t have_row = FALSE;
15092   svn_revnum_t revision;
15093   apr_int64_t repos_id;
15094   const char *repos_relpath;
15095   svn_node_kind_t kind;
15096   int op_depth = relpath_depth(local_relpath);
15097
15098   if (last_op_depth != op_depth)
15099     {
15100       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15101                                         STMT_SELECT_DEPTH_NODE));
15102       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15103                                 op_depth));
15104       SVN_ERR(svn_sqlite__step(&have_row, stmt));
15105       SVN_ERR(svn_sqlite__reset(stmt));
15106       if (have_row)
15107         shadowed = TRUE;
15108     }
15109
15110   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, &kind, &revision,
15111                                             &repos_relpath, &repos_id, NULL,
15112                                             NULL, NULL, NULL, NULL, NULL, NULL,
15113                                             NULL, NULL, NULL,
15114                                             wcroot, local_relpath,
15115                                             scratch_pool, scratch_pool));
15116
15117   if (last_repos_relpath
15118       && repos_id == last_repos_id
15119       && revision == last_revision)
15120     {
15121       const char *name = svn_relpath_skip_ancestor(last_repos_relpath,
15122                                                    repos_relpath);
15123
15124       if (name && strcmp(name, svn_relpath_basename(local_relpath, NULL)) == 0)
15125         op_depth = last_op_depth;
15126     }
15127
15128   /* Can we add a new copy node at the wanted op-depth? */
15129   if (!have_row || op_depth == last_op_depth)
15130     {
15131       int i;
15132
15133       SVN_ERR(svn_sqlite__get_statement(
15134                     &stmt, wcroot->sdb,
15135                     STMT_INSERT_WORKING_NODE_FROM_BASE_COPY));
15136       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15137                                 op_depth));
15138       SVN_ERR(svn_sqlite__step_done(stmt));
15139
15140       if (shadowed)
15141         SVN_ERR(db_extend_parent_delete(wcroot, local_relpath, kind,
15142                                         op_depth, scratch_pool));
15143
15144       if (kind == svn_node_dir)
15145         {
15146           const apr_array_header_t *children;
15147           apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15148
15149           SVN_ERR(gather_children(&children, wcroot, local_relpath,
15150                                   STMT_SELECT_OP_DEPTH_CHILDREN, 0,
15151                                   scratch_pool, iterpool));
15152
15153           for (i = 0; i < children->nelts; i++)
15154             {
15155               const char *name = APR_ARRAY_IDX(children, i, const char *);
15156               const char *copy_relpath;
15157
15158               svn_pool_clear(iterpool);
15159
15160               copy_relpath = svn_relpath_join(local_relpath, name, iterpool);
15161
15162               SVN_ERR(make_copy_txn(wcroot, copy_relpath,
15163                                     repos_id, repos_relpath, revision,
15164                                     op_depth, shadowed, root_shadow_depth,
15165                                     scratch_pool));
15166             }
15167           svn_pool_destroy(iterpool);
15168         }
15169     }
15170   else
15171     {
15172       /* Auch... we can't make a copy of whatever comes deeper, as this
15173          op-depth is already filled by something else. Let's hope
15174          the user doesn't mind.
15175
15176          Luckily we know that the moves are already moved to the shadowing
15177          layer, so we can just remove dangling base-deletes if there are
15178          any.
15179        */
15180       /* BASE_DELETED may be at op_depth, so let's use last_op_depth! */
15181       SVN_ERR(db_move_moved_to_down_recursive(wcroot, local_relpath,
15182                                               root_shadow_depth,
15183                                               scratch_pool));
15184
15185       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15186                     STMT_DELETE_WORKING_BASE_DELETE));
15187       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15188                                 last_op_depth));
15189       SVN_ERR(svn_sqlite__step_done(stmt));
15190       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15191                     STMT_DELETE_WORKING_BASE_DELETE_RECURSIVE));
15192       SVN_ERR(svn_sqlite__bindf(stmt, "isd", wcroot->wc_id, local_relpath,
15193                                 last_op_depth));
15194       SVN_ERR(svn_sqlite__step_done(stmt));
15195     }
15196
15197   /* Insert a not-present node to mark that we don't know what exists here.
15198
15199      We do this last (after recursing), to allow the move fix-up code to
15200      see the original moves. */
15201   if (last_op_depth > 0 && last_op_depth != op_depth)
15202     {
15203       insert_working_baton_t iwb;
15204
15205       blank_iwb(&iwb);
15206       iwb.presence = svn_wc__db_status_not_present;
15207       iwb.op_depth = last_op_depth;
15208
15209       iwb.original_repos_id = repos_id;
15210       iwb.original_repos_relpath = repos_relpath;
15211       iwb.original_revnum = revision;
15212       iwb.kind = kind;
15213
15214       SVN_ERR(insert_working_node(&iwb, wcroot, local_relpath, scratch_pool));
15215     }
15216
15217   return SVN_NO_ERROR;
15218 }
15219
15220
15221 svn_error_t *
15222 svn_wc__db_op_make_copy_internal(svn_wc__db_wcroot_t *wcroot,
15223                                  const char *local_relpath,
15224                                  svn_boolean_t move_move_info,
15225                                  const svn_skel_t *conflicts,
15226                                  const svn_skel_t *work_items,
15227                                  apr_pool_t *scratch_pool)
15228 {
15229   svn_sqlite__stmt_t *stmt;
15230   svn_boolean_t have_row;
15231   int op_depth = -1;
15232
15233   /* The update editor is supposed to call this function when there is
15234      no working node for LOCAL_ABSPATH. */
15235   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15236                                     STMT_SELECT_WORKING_NODE));
15237   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15238   SVN_ERR(svn_sqlite__step(&have_row, stmt));
15239   if (have_row)
15240     op_depth = svn_sqlite__column_int(stmt, 0);
15241   SVN_ERR(svn_sqlite__reset(stmt));
15242
15243   if (have_row)
15244     {
15245       if (op_depth == relpath_depth(local_relpath))
15246         return svn_error_createf(SVN_ERR_WC_PATH_UNEXPECTED_STATUS, NULL,
15247                              _("Modification of '%s' already exists"),
15248                              path_for_error_message(wcroot,
15249                                                     local_relpath,
15250                                                     scratch_pool));
15251
15252       /* We have a working layer, but not one at the op-depth of local-relpath,
15253          so we can create a copy by just copying the lower layer */
15254
15255       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15256                                         STMT_COPY_OP_DEPTH_RECURSIVE));
15257       SVN_ERR(svn_sqlite__bindf(stmt, "isdd", wcroot->wc_id, local_relpath,
15258                                 op_depth, relpath_depth(local_relpath)));
15259       SVN_ERR(svn_sqlite__step_done(stmt));
15260     }
15261   else
15262     {
15263       int affected_rows;
15264
15265       op_depth = relpath_depth(local_relpath);
15266       /* We don't allow copies to contain server-excluded nodes;
15267          the update editor is going to have to bail out. */
15268       SVN_ERR(catch_copy_of_server_excluded(wcroot, local_relpath,
15269                                             scratch_pool));
15270
15271       /* Insert a shadowing layer */
15272       SVN_ERR(svn_sqlite__get_statement(
15273                         &stmt, wcroot->sdb,
15274                         STMT_INSERT_DELETE_FROM_NODE_RECURSIVE));
15275
15276       /* As we are keeping whatever is below, move the*/
15277
15278       SVN_ERR(svn_sqlite__bindf(stmt, "isdd",
15279                                 wcroot->wc_id, local_relpath,
15280                                 0, op_depth));
15281       SVN_ERR(svn_sqlite__update(&affected_rows, stmt));
15282       SVN_ERR_ASSERT(affected_rows > 0);
15283
15284       if (!move_move_info)
15285         SVN_ERR(db_move_moved_to_down_recursive(wcroot, local_relpath,
15286                                                 op_depth, scratch_pool));
15287
15288
15289       SVN_ERR(make_copy_txn(wcroot, local_relpath,
15290                             INVALID_REPOS_ID, NULL, SVN_INVALID_REVNUM,
15291                             op_depth, FALSE, op_depth,
15292                             scratch_pool));
15293     }
15294
15295   if (conflicts)
15296     SVN_ERR(svn_wc__db_mark_conflict_internal(wcroot, local_relpath,
15297                                               conflicts, scratch_pool));
15298
15299   SVN_ERR(add_work_items(wcroot->sdb, work_items, scratch_pool));
15300
15301   return SVN_NO_ERROR;
15302 }
15303
15304
15305 svn_error_t *
15306 svn_wc__db_op_make_copy(svn_wc__db_t *db,
15307                         const char *local_abspath,
15308                         const svn_skel_t *conflicts,
15309                         const svn_skel_t *work_items,
15310                         apr_pool_t *scratch_pool)
15311 {
15312   svn_wc__db_wcroot_t *wcroot;
15313   const char *local_relpath;
15314
15315   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15316
15317   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15318                               local_abspath, scratch_pool, scratch_pool));
15319   VERIFY_USABLE_WCROOT(wcroot);
15320
15321   SVN_WC__DB_WITH_TXN(
15322     svn_wc__db_op_make_copy_internal(wcroot, local_relpath, FALSE,
15323                                      conflicts, work_items,
15324                                      scratch_pool),
15325     wcroot);
15326
15327   SVN_ERR(flush_entries(wcroot, local_abspath,
15328                         svn_depth_infinity, scratch_pool));
15329
15330   return SVN_NO_ERROR;
15331 }
15332
15333 svn_error_t *
15334 svn_wc__db_info_below_working(svn_boolean_t *have_base,
15335                               svn_boolean_t *have_work,
15336                               svn_wc__db_status_t *status,
15337                               svn_wc__db_t *db,
15338                               const char *local_abspath,
15339                               apr_pool_t *scratch_pool)
15340 {
15341   svn_wc__db_wcroot_t *wcroot;
15342   const char *local_relpath;
15343
15344   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15345
15346   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15347                               local_abspath, scratch_pool, scratch_pool));
15348   VERIFY_USABLE_WCROOT(wcroot);
15349   SVN_ERR(info_below_working(have_base, have_work, status,
15350                              wcroot, local_relpath, -1, scratch_pool));
15351
15352   return SVN_NO_ERROR;
15353 }
15354
15355 svn_error_t *
15356 svn_wc__db_get_not_present_descendants(const apr_array_header_t **descendants,
15357                                        svn_wc__db_t *db,
15358                                        const char *local_abspath,
15359                                        apr_pool_t *result_pool,
15360                                        apr_pool_t *scratch_pool)
15361 {
15362   svn_wc__db_wcroot_t *wcroot;
15363   const char *local_relpath;
15364   svn_sqlite__stmt_t *stmt;
15365   svn_boolean_t have_row;
15366
15367   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15368
15369   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15370                               local_abspath, scratch_pool, scratch_pool));
15371   VERIFY_USABLE_WCROOT(wcroot);
15372
15373   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15374                                     STMT_SELECT_NOT_PRESENT_DESCENDANTS));
15375
15376   SVN_ERR(svn_sqlite__bindf(stmt, "isd",
15377                             wcroot->wc_id,
15378                             local_relpath,
15379                             relpath_depth(local_relpath)));
15380
15381   SVN_ERR(svn_sqlite__step(&have_row, stmt));
15382
15383   if (have_row)
15384     {
15385       apr_array_header_t *paths;
15386
15387       paths = apr_array_make(result_pool, 4, sizeof(const char*));
15388       while (have_row)
15389         {
15390           const char *found_relpath = svn_sqlite__column_text(stmt, 0, NULL);
15391
15392           APR_ARRAY_PUSH(paths, const char *)
15393               = apr_pstrdup(result_pool, svn_relpath_skip_ancestor(
15394                                            local_relpath, found_relpath));
15395
15396           SVN_ERR(svn_sqlite__step(&have_row, stmt));
15397         }
15398
15399       *descendants = paths;
15400     }
15401   else
15402     *descendants = apr_array_make(result_pool, 0, sizeof(const char*));
15403
15404   return svn_error_trace(svn_sqlite__reset(stmt));
15405 }
15406
15407
15408 /* Like svn_wc__db_min_max_revisions(),
15409  * but accepts a WCROOT/LOCAL_RELPATH pair. */
15410 static svn_error_t *
15411 get_min_max_revisions(svn_revnum_t *min_revision,
15412                       svn_revnum_t *max_revision,
15413                       svn_wc__db_wcroot_t *wcroot,
15414                       const char *local_relpath,
15415                       svn_boolean_t committed,
15416                       apr_pool_t *scratch_pool)
15417 {
15418   svn_sqlite__stmt_t *stmt;
15419   svn_revnum_t min_rev, max_rev;
15420
15421   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15422                                     STMT_SELECT_MIN_MAX_REVISIONS));
15423   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15424   SVN_ERR(svn_sqlite__step_row(stmt));
15425
15426   if (committed)
15427     {
15428       min_rev = svn_sqlite__column_revnum(stmt, 2);
15429       max_rev = svn_sqlite__column_revnum(stmt, 3);
15430     }
15431   else
15432     {
15433       min_rev = svn_sqlite__column_revnum(stmt, 0);
15434       max_rev = svn_sqlite__column_revnum(stmt, 1);
15435     }
15436
15437   /* The statement returns exactly one row. */
15438   SVN_ERR(svn_sqlite__reset(stmt));
15439
15440   if (min_revision)
15441     *min_revision = min_rev;
15442   if (max_revision)
15443     *max_revision = max_rev;
15444
15445   return SVN_NO_ERROR;
15446 }
15447
15448
15449 svn_error_t *
15450 svn_wc__db_min_max_revisions(svn_revnum_t *min_revision,
15451                              svn_revnum_t *max_revision,
15452                              svn_wc__db_t *db,
15453                              const char *local_abspath,
15454                              svn_boolean_t committed,
15455                              apr_pool_t *scratch_pool)
15456 {
15457   svn_wc__db_wcroot_t *wcroot;
15458   const char *local_relpath;
15459
15460   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15461
15462   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15463                                                 db, local_abspath,
15464                                                 scratch_pool, scratch_pool));
15465   VERIFY_USABLE_WCROOT(wcroot);
15466
15467   return svn_error_trace(get_min_max_revisions(min_revision, max_revision,
15468                                                wcroot, local_relpath,
15469                                                committed, scratch_pool));
15470 }
15471
15472
15473 /* Set *IS_SPARSE_CHECKOUT TRUE if LOCAL_RELPATH or any of the nodes
15474  * within LOCAL_RELPATH is sparse, FALSE otherwise. */
15475 static svn_error_t *
15476 is_sparse_checkout_internal(svn_boolean_t *is_sparse_checkout,
15477                             svn_wc__db_wcroot_t *wcroot,
15478                             const char *local_relpath,
15479                             apr_pool_t *scratch_pool)
15480 {
15481   svn_sqlite__stmt_t *stmt;
15482   svn_boolean_t have_row;
15483
15484   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15485                                     STMT_HAS_SPARSE_NODES));
15486   SVN_ERR(svn_sqlite__bindf(stmt, "is",
15487                             wcroot->wc_id,
15488                             local_relpath));
15489   /* If this query returns a row, the working copy is sparse. */
15490   SVN_ERR(svn_sqlite__step(&have_row, stmt));
15491   *is_sparse_checkout = have_row;
15492   SVN_ERR(svn_sqlite__reset(stmt));
15493
15494   return SVN_NO_ERROR;
15495 }
15496
15497
15498 /* Like svn_wc__db_has_switched_subtrees(),
15499  * but accepts a WCROOT/LOCAL_RELPATH pair. */
15500 static svn_error_t *
15501 has_switched_subtrees(svn_boolean_t *is_switched,
15502                       svn_wc__db_wcroot_t *wcroot,
15503                       const char *local_relpath,
15504                       const char *trail_url,
15505                       apr_pool_t *scratch_pool)
15506 {
15507   svn_sqlite__stmt_t *stmt;
15508   svn_boolean_t have_row;
15509   apr_int64_t repos_id;
15510   const char *repos_relpath;
15511
15512   /* Optional argument handling for caller */
15513   if (!is_switched)
15514     return SVN_NO_ERROR;
15515
15516   *is_switched = FALSE;
15517
15518   SVN_ERR(svn_wc__db_base_get_info_internal(NULL, NULL, NULL,
15519                                             &repos_relpath, &repos_id,
15520                                             NULL, NULL, NULL, NULL, NULL,
15521                                             NULL, NULL, NULL, NULL, NULL,
15522                                             wcroot, local_relpath,
15523                                             scratch_pool, scratch_pool));
15524
15525   /* First do the cheap check where we only need info on the origin itself */
15526   if (trail_url != NULL)
15527     {
15528       const char *repos_root_url;
15529       const char *url;
15530       apr_size_t len1, len2;
15531
15532       /* If the trailing part of the URL of the working copy directory
15533          does not match the given trailing URL then the whole working
15534          copy is switched. */
15535
15536       SVN_ERR(svn_wc__db_fetch_repos_info(&repos_root_url, NULL, wcroot,
15537                                           repos_id, scratch_pool));
15538       url = svn_path_url_add_component2(repos_root_url, repos_relpath,
15539                                         scratch_pool);
15540
15541       len1 = strlen(trail_url);
15542       len2 = strlen(url);
15543       if ((len1 > len2) || strcmp(url + len2 - len1, trail_url))
15544         {
15545           *is_switched = TRUE;
15546           return SVN_NO_ERROR;
15547         }
15548     }
15549
15550   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_HAS_SWITCHED));
15551   SVN_ERR(svn_sqlite__bindf(stmt, "iss", wcroot->wc_id, local_relpath, repos_relpath));
15552   SVN_ERR(svn_sqlite__step(&have_row, stmt));
15553   if (have_row)
15554     *is_switched = TRUE;
15555   SVN_ERR(svn_sqlite__reset(stmt));
15556
15557   return SVN_NO_ERROR;
15558 }
15559
15560
15561 svn_error_t *
15562 svn_wc__db_has_switched_subtrees(svn_boolean_t *is_switched,
15563                                  svn_wc__db_t *db,
15564                                  const char *local_abspath,
15565                                  const char *trail_url,
15566                                  apr_pool_t *scratch_pool)
15567 {
15568   svn_wc__db_wcroot_t *wcroot;
15569   const char *local_relpath;
15570
15571   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15572
15573   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15574                                                 db, local_abspath,
15575                                                 scratch_pool, scratch_pool));
15576   VERIFY_USABLE_WCROOT(wcroot);
15577
15578   return svn_error_trace(has_switched_subtrees(is_switched, wcroot,
15579                                                local_relpath, trail_url,
15580                                                scratch_pool));
15581 }
15582
15583 svn_error_t *
15584 svn_wc__db_get_excluded_subtrees(apr_hash_t **excluded_subtrees,
15585                                  svn_wc__db_t *db,
15586                                  const char *local_abspath,
15587                                  apr_pool_t *result_pool,
15588                                  apr_pool_t *scratch_pool)
15589 {
15590   svn_wc__db_wcroot_t *wcroot;
15591   const char *local_relpath;
15592   svn_sqlite__stmt_t *stmt;
15593   svn_boolean_t have_row;
15594
15595   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15596   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15597                                                 db, local_abspath,
15598                                                 scratch_pool, scratch_pool));
15599   VERIFY_USABLE_WCROOT(wcroot);
15600
15601   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15602                                     STMT_SELECT_ALL_EXCLUDED_DESCENDANTS));
15603   SVN_ERR(svn_sqlite__bindf(stmt, "is",
15604                             wcroot->wc_id,
15605                             local_relpath));
15606   SVN_ERR(svn_sqlite__step(&have_row, stmt));
15607
15608   if (have_row)
15609     *excluded_subtrees = apr_hash_make(result_pool);
15610   else
15611     *excluded_subtrees = NULL;
15612
15613   while (have_row)
15614     {
15615       const char *abs_path =
15616         svn_dirent_join(wcroot->abspath,
15617                         svn_sqlite__column_text(stmt, 0, NULL),
15618                         result_pool);
15619       svn_hash_sets(*excluded_subtrees, abs_path, abs_path);
15620       SVN_ERR(svn_sqlite__step(&have_row, stmt));
15621     }
15622
15623   SVN_ERR(svn_sqlite__reset(stmt));
15624   return SVN_NO_ERROR;
15625 }
15626
15627 /* Like svn_wc__db_has_db_mods(),
15628  * but accepts a WCROOT/LOCAL_RELPATH pair.
15629  * ### This needs a DB as well as a WCROOT/RELPATH pair... */
15630 static svn_error_t *
15631 has_db_mods(svn_boolean_t *is_modified,
15632             svn_wc__db_wcroot_t *wcroot,
15633             const char *local_relpath,
15634             apr_pool_t *scratch_pool)
15635 {
15636   svn_sqlite__stmt_t *stmt;
15637
15638   /* Check for additions or deletions. */
15639   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15640                                     STMT_SUBTREE_HAS_TREE_MODIFICATIONS));
15641   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15642   /* If this query returns a row, the working copy is modified. */
15643   SVN_ERR(svn_sqlite__step(is_modified, stmt));
15644   SVN_ERR(svn_sqlite__reset(stmt));
15645
15646   if (! *is_modified)
15647     {
15648       /* Check for property modifications. */
15649       SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15650                                         STMT_SUBTREE_HAS_PROP_MODIFICATIONS));
15651       SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15652       /* If this query returns a row, the working copy is modified. */
15653       SVN_ERR(svn_sqlite__step(is_modified, stmt));
15654       SVN_ERR(svn_sqlite__reset(stmt));
15655     }
15656
15657   return SVN_NO_ERROR;
15658 }
15659
15660
15661 svn_error_t *
15662 svn_wc__db_has_db_mods(svn_boolean_t *is_modified,
15663                        svn_wc__db_t *db,
15664                        const char *local_abspath,
15665                        apr_pool_t *scratch_pool)
15666 {
15667   svn_wc__db_wcroot_t *wcroot;
15668   const char *local_relpath;
15669
15670   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15671
15672   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15673                                                 db, local_abspath,
15674                                                 scratch_pool, scratch_pool));
15675   VERIFY_USABLE_WCROOT(wcroot);
15676
15677   return svn_error_trace(has_db_mods(is_modified, wcroot, local_relpath,
15678                                      scratch_pool));
15679 }
15680
15681
15682 /* The body of svn_wc__db_revision_status().
15683  */
15684 static svn_error_t *
15685 revision_status_txn(svn_revnum_t *min_revision,
15686                     svn_revnum_t *max_revision,
15687                     svn_boolean_t *is_sparse_checkout,
15688                     svn_boolean_t *is_modified,
15689                     svn_boolean_t *is_switched,
15690                     svn_wc__db_wcroot_t *wcroot,
15691                     const char *local_relpath,
15692                     svn_wc__db_t *db,
15693                     const char *trail_url,
15694                     svn_boolean_t committed,
15695                     apr_pool_t *scratch_pool)
15696 {
15697   svn_error_t *err;
15698   svn_boolean_t exists;
15699
15700   SVN_ERR(does_node_exist(&exists, wcroot, local_relpath));
15701
15702   if (!exists)
15703     {
15704       return svn_error_createf(SVN_ERR_WC_PATH_NOT_FOUND, NULL,
15705                                _("The node '%s' was not found."),
15706                                path_for_error_message(wcroot, local_relpath,
15707                                                       scratch_pool));
15708     }
15709
15710   /* Determine mixed-revisionness. */
15711   SVN_ERR(get_min_max_revisions(min_revision, max_revision, wcroot,
15712                                 local_relpath, committed, scratch_pool));
15713
15714   /* Determine sparseness. */
15715   SVN_ERR(is_sparse_checkout_internal(is_sparse_checkout, wcroot,
15716                                       local_relpath, scratch_pool));
15717
15718   /* Check for switched nodes. */
15719   {
15720     err = has_switched_subtrees(is_switched, wcroot, local_relpath,
15721                                 trail_url, scratch_pool);
15722
15723     if (err)
15724       {
15725         if (err->apr_err != SVN_ERR_WC_PATH_NOT_FOUND)
15726           return svn_error_trace(err);
15727
15728         svn_error_clear(err); /* No Base node, but no fatal error */
15729         *is_switched = FALSE;
15730       }
15731   }
15732
15733   /* Check for db mods. */
15734   SVN_ERR(has_db_mods(is_modified, wcroot, local_relpath, scratch_pool));
15735
15736   return SVN_NO_ERROR;
15737 }
15738
15739
15740 svn_error_t *
15741 svn_wc__db_revision_status(svn_revnum_t *min_revision,
15742                            svn_revnum_t *max_revision,
15743                            svn_boolean_t *is_sparse_checkout,
15744                            svn_boolean_t *is_modified,
15745                            svn_boolean_t *is_switched,
15746                            svn_wc__db_t *db,
15747                            const char *local_abspath,
15748                            const char *trail_url,
15749                            svn_boolean_t committed,
15750                            apr_pool_t *scratch_pool)
15751 {
15752   svn_wc__db_wcroot_t *wcroot;
15753   const char *local_relpath;
15754
15755   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15756
15757   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15758                                                 db, local_abspath,
15759                                                 scratch_pool, scratch_pool));
15760   VERIFY_USABLE_WCROOT(wcroot);
15761
15762   SVN_WC__DB_WITH_TXN(
15763     revision_status_txn(min_revision, max_revision,
15764                         is_sparse_checkout, is_modified, is_switched,
15765                         wcroot, local_relpath, db,
15766                         trail_url, committed,
15767                         scratch_pool),
15768     wcroot);
15769   return SVN_NO_ERROR;
15770 }
15771
15772
15773 svn_error_t *
15774 svn_wc__db_base_get_lock_tokens_recursive(apr_hash_t **lock_tokens,
15775                                           svn_wc__db_t *db,
15776                                           const char *local_abspath,
15777                                           apr_pool_t *result_pool,
15778                                           apr_pool_t *scratch_pool)
15779 {
15780   svn_wc__db_wcroot_t *wcroot;
15781   const char *local_relpath;
15782   svn_sqlite__stmt_t *stmt;
15783   svn_boolean_t have_row;
15784   apr_int64_t last_repos_id = INVALID_REPOS_ID;
15785   const char *last_repos_root_url = NULL;
15786
15787   SVN_ERR_ASSERT(svn_dirent_is_absolute(local_abspath));
15788
15789   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15790                                                 db, local_abspath,
15791                                                 scratch_pool, scratch_pool));
15792   VERIFY_USABLE_WCROOT(wcroot);
15793
15794   *lock_tokens = apr_hash_make(result_pool);
15795
15796   /* Fetch all the lock tokens in and under LOCAL_RELPATH. */
15797   SVN_ERR(svn_sqlite__get_statement(
15798               &stmt, wcroot->sdb,
15799               STMT_SELECT_BASE_NODE_LOCK_TOKENS_RECURSIVE));
15800
15801   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, local_relpath));
15802   SVN_ERR(svn_sqlite__step(&have_row, stmt));
15803   while (have_row)
15804     {
15805       apr_int64_t child_repos_id = svn_sqlite__column_int64(stmt, 0);
15806       const char *child_relpath = svn_sqlite__column_text(stmt, 1, NULL);
15807       const char *lock_token = svn_sqlite__column_text(stmt, 2, result_pool);
15808
15809       if (child_repos_id != last_repos_id)
15810         {
15811           svn_error_t *err = svn_wc__db_fetch_repos_info(&last_repos_root_url,
15812                                                          NULL, wcroot,
15813                                                          child_repos_id,
15814                                                          scratch_pool);
15815
15816           if (err)
15817             {
15818               return svn_error_trace(
15819                             svn_error_compose_create(err,
15820                                                      svn_sqlite__reset(stmt)));
15821             }
15822
15823           last_repos_id = child_repos_id;
15824         }
15825
15826       SVN_ERR_ASSERT(last_repos_root_url != NULL);
15827       svn_hash_sets(*lock_tokens,
15828                     svn_path_url_add_component2(last_repos_root_url,
15829                                                 child_relpath, result_pool),
15830                     lock_token);
15831
15832       SVN_ERR(svn_sqlite__step(&have_row, stmt));
15833     }
15834   return svn_sqlite__reset(stmt);
15835 }
15836
15837
15838 /* If EXPRESSION is false, cause the caller to return an SVN_ERR_WC_CORRUPT
15839  * error, showing EXPRESSION and the caller's LOCAL_RELPATH in the message. */
15840 #define VERIFY(expression) \
15841   do { \
15842     if (! (expression)) \
15843       return svn_error_createf(SVN_ERR_WC_CORRUPT, NULL, \
15844         _("database inconsistency at local_relpath='%s' verifying " \
15845           "expression '%s'"), local_relpath, #expression); \
15846   } while (0)
15847
15848
15849 /* Verify consistency of the metadata concerning WCROOT.  This is intended
15850  * for use only during testing and debugging, so is not intended to be
15851  * blazingly fast.
15852  *
15853  * This code is a complement to any verification that we can do in SQLite
15854  * triggers.  See, for example, 'wc-checks.sql'.
15855  *
15856  * Some more verification steps we might want to add are:
15857  *
15858  *   * on every ACTUAL row (except root): a NODES row exists at its parent path
15859  *   * the op-depth root must always exist and every intermediate too
15860  */
15861 static svn_error_t *
15862 verify_wcroot(svn_wc__db_wcroot_t *wcroot,
15863               apr_pool_t *scratch_pool)
15864 {
15865   svn_sqlite__stmt_t *stmt;
15866   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15867
15868   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
15869                                     STMT_SELECT_ALL_NODES));
15870   SVN_ERR(svn_sqlite__bindf(stmt, "i", wcroot->wc_id));
15871   while (TRUE)
15872     {
15873       svn_boolean_t have_row;
15874       const char *local_relpath, *parent_relpath;
15875       int op_depth;
15876
15877       svn_pool_clear(iterpool);
15878
15879       SVN_ERR(svn_sqlite__step(&have_row, stmt));
15880       if (!have_row)
15881         break;
15882
15883       op_depth = svn_sqlite__column_int(stmt, 0);
15884       local_relpath = svn_sqlite__column_text(stmt, 1, iterpool);
15885       parent_relpath = svn_sqlite__column_text(stmt, 2, iterpool);
15886
15887       /* Verify parent_relpath is the parent path of local_relpath */
15888       VERIFY((parent_relpath == NULL)
15889              ? (local_relpath[0] == '\0')
15890              : (strcmp(svn_relpath_dirname(local_relpath, iterpool),
15891                        parent_relpath) == 0));
15892
15893       /* Verify op_depth <= the tree depth of local_relpath */
15894       VERIFY(op_depth <= relpath_depth(local_relpath));
15895
15896       /* Verify parent_relpath refers to a row that exists */
15897       /* TODO: Verify there is a suitable parent row - e.g. has op_depth <=
15898        * the child's and a suitable presence */
15899       if (parent_relpath && svn_sqlite__column_is_null(stmt, 3))
15900         {
15901           svn_sqlite__stmt_t *stmt2;
15902           svn_boolean_t have_a_parent_row;
15903
15904           SVN_ERR(svn_sqlite__get_statement(&stmt2, wcroot->sdb,
15905                                             STMT_SELECT_NODE_INFO));
15906           SVN_ERR(svn_sqlite__bindf(stmt2, "is", wcroot->wc_id,
15907                                     parent_relpath));
15908           SVN_ERR(svn_sqlite__step(&have_a_parent_row, stmt2));
15909           VERIFY(have_a_parent_row);
15910           SVN_ERR(svn_sqlite__reset(stmt2));
15911         }
15912     }
15913   svn_pool_destroy(iterpool);
15914
15915   return svn_error_trace(svn_sqlite__reset(stmt));
15916 }
15917
15918 svn_error_t *
15919 svn_wc__db_verify(svn_wc__db_t *db,
15920                   const char *wri_abspath,
15921                   apr_pool_t *scratch_pool)
15922 {
15923   svn_wc__db_wcroot_t *wcroot;
15924   const char *local_relpath;
15925
15926   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
15927                                                 db, wri_abspath,
15928                                                 scratch_pool, scratch_pool));
15929   VERIFY_USABLE_WCROOT(wcroot);
15930
15931   SVN_ERR(verify_wcroot(wcroot, scratch_pool));
15932   return SVN_NO_ERROR;
15933 }
15934
15935
15936 svn_error_t *
15937 svn_wc__db_verify_db_full_internal(svn_wc__db_wcroot_t *wcroot,
15938                                    svn_wc__db_verify_cb_t callback,
15939                                    void *baton,
15940                                    apr_pool_t *scratch_pool)
15941 {
15942   svn_sqlite__stmt_t *stmt;
15943   svn_boolean_t have_row;
15944   svn_error_t *err = NULL;
15945   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
15946
15947   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb, STMT_STATIC_VERIFY));
15948   SVN_ERR(svn_sqlite__step(&have_row, stmt));
15949
15950   while (have_row)
15951     {
15952       const char *local_relpath;
15953       int op_depth = svn_sqlite__column_int(stmt, 1);
15954       int id = svn_sqlite__column_int(stmt, 2);
15955       const char *msg;
15956
15957       svn_pool_clear(iterpool);
15958
15959       local_relpath =  svn_sqlite__column_text(stmt, 0, iterpool);
15960       msg = svn_sqlite__column_text(stmt, 3, scratch_pool);
15961
15962       err = callback(baton, wcroot->abspath, local_relpath, op_depth,
15963                      id, msg, iterpool);
15964
15965       if (err)
15966         break;
15967
15968       SVN_ERR(svn_sqlite__step(&have_row, stmt));
15969     }
15970
15971   svn_pool_destroy(iterpool);
15972
15973   return svn_error_trace(
15974             svn_error_compose_create(err, svn_sqlite__reset(stmt)));
15975 }
15976
15977 svn_error_t *
15978 svn_wc__db_verify_db_full(svn_wc__db_t *db,
15979                           const char *wri_abspath,
15980                           svn_wc__db_verify_cb_t callback,
15981                           void *baton,
15982                           apr_pool_t *scratch_pool)
15983 {
15984   svn_wc__db_wcroot_t *wcroot;
15985   const char *local_relpath;
15986
15987   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
15988
15989   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
15990                               wri_abspath, scratch_pool, scratch_pool));
15991   VERIFY_USABLE_WCROOT(wcroot);
15992
15993   return svn_error_trace(
15994             svn_wc__db_verify_db_full_internal(wcroot, callback, baton,
15995                                                scratch_pool));
15996 }
15997
15998 svn_error_t *
15999 svn_wc__db_bump_format(int *result_format,
16000                        svn_boolean_t *bumped_format,
16001                        svn_wc__db_t *db,
16002                        const char *wcroot_abspath,
16003                        apr_pool_t *scratch_pool)
16004 {
16005   svn_sqlite__db_t *sdb;
16006   svn_error_t *err;
16007   int format;
16008
16009   if (bumped_format)
16010     *bumped_format = FALSE;
16011
16012   /* Do not scan upwards for a working copy root here to prevent accidental
16013    * upgrades of any working copies the WCROOT might be nested in.
16014    * Just try to open a DB at the specified path instead. */
16015   err = svn_wc__db_util_open_db(&sdb, wcroot_abspath, SDB_FILE,
16016                                 svn_sqlite__mode_readwrite,
16017                                 TRUE, /* exclusive */
16018                                 0, /* default timeout */
16019                                 NULL, /* my statements */
16020                                 scratch_pool, scratch_pool);
16021   if (err)
16022     {
16023       svn_error_t *err2;
16024       apr_hash_t *entries;
16025
16026       /* Could not open an sdb. Check for an entries file instead. */
16027       err2 = svn_wc__read_entries_old(&entries, wcroot_abspath,
16028                                       scratch_pool, scratch_pool);
16029       if (err2 || apr_hash_count(entries) == 0)
16030         return svn_error_createf(SVN_ERR_WC_INVALID_OP_ON_CWD,
16031                   svn_error_compose_create(err, err2),
16032                   _("Can't upgrade '%s' as it is not a working copy root"),
16033                   svn_dirent_local_style(wcroot_abspath, scratch_pool));
16034
16035       /* An entries file was found. This is a pre-wc-ng working copy
16036        * so suggest an upgrade. */
16037       return svn_error_createf(SVN_ERR_WC_UPGRADE_REQUIRED, err,
16038                 _("Working copy '%s' is too old and must be upgraded to "
16039                   "at least format %d, as created by Subversion %s"),
16040                 svn_dirent_local_style(wcroot_abspath, scratch_pool),
16041                 SVN_WC__WC_NG_VERSION,
16042                 svn_wc__version_string_from_format(SVN_WC__WC_NG_VERSION));
16043     }
16044
16045   SVN_ERR(svn_sqlite__read_schema_version(&format, sdb, scratch_pool));
16046   err = svn_wc__upgrade_sdb(result_format, wcroot_abspath,
16047                             sdb, format, scratch_pool);
16048
16049   if (err == SVN_NO_ERROR && bumped_format)
16050     *bumped_format = (*result_format > format);
16051
16052   /* Make sure we return a different error than expected for upgrades from
16053      entries */
16054   if (err && err->apr_err == SVN_ERR_WC_UPGRADE_REQUIRED)
16055     err = svn_error_create(SVN_ERR_WC_UNSUPPORTED_FORMAT, err,
16056                            _("Working copy upgrade failed"));
16057
16058   err = svn_error_compose_create(err, svn_sqlite__close(sdb));
16059
16060   return svn_error_trace(err);
16061 }
16062
16063 svn_error_t *
16064 svn_wc__db_vacuum(svn_wc__db_t *db,
16065                   const char *local_abspath,
16066                   apr_pool_t *scratch_pool)
16067 {
16068   svn_wc__db_wcroot_t *wcroot;
16069   const char *local_relpath;
16070
16071   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath,
16072                                                 db, local_abspath,
16073                                                 scratch_pool, scratch_pool));
16074   SVN_ERR(svn_sqlite__exec_statements(wcroot->sdb, STMT_VACUUM));
16075
16076   return SVN_NO_ERROR;
16077 }
16078
16079 /* Item queued with svn_wc__db_commit_queue_add */
16080 typedef struct commit_queue_item_t
16081 {
16082   const char *local_relpath;
16083   svn_boolean_t recurse; /* Use legacy recursion */
16084   svn_boolean_t committed; /* Process the node as committed */
16085   svn_boolean_t remove_lock; /* Remove existing lock on node */
16086   svn_boolean_t remove_changelist; /* Remove changelist on node */
16087
16088   /* The pristine text checksum. NULL if the old value should be kept
16089      and for directories */
16090   const svn_checksum_t *new_sha1_checksum;
16091
16092   apr_hash_t *new_dav_cache; /* New DAV cache for the node */
16093 } commit_queue_item_t;
16094
16095 /* The queue definition for vn_wc__db_create_commit_queue,
16096    svn_wc__db_commit_queue_add and finally svn_wc__db_process_commit_queue */
16097 struct svn_wc__db_commit_queue_t
16098 {
16099   svn_wc__db_wcroot_t *wcroot; /* Wcroot for ITEMS */
16100   apr_array_header_t *items; /* List of commit_queue_item_t* */
16101   svn_boolean_t have_recurse; /* Is one or more item[x]->recurse TRUE? */
16102 };
16103
16104 /* Create a new svn_wc__db_commit_queue_t instance in RESULT_POOL for the
16105    working copy specified with WRI_ABSPATH */
16106 svn_error_t *
16107 svn_wc__db_create_commit_queue(svn_wc__db_commit_queue_t **queue,
16108                                svn_wc__db_t *db,
16109                                const char *wri_abspath,
16110                                apr_pool_t *result_pool,
16111                                apr_pool_t *scratch_pool)
16112 {
16113   svn_wc__db_wcroot_t *wcroot;
16114   const char *local_relpath;
16115   svn_wc__db_commit_queue_t *q;
16116
16117   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
16118
16119   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &local_relpath, db,
16120                               wri_abspath, result_pool, scratch_pool));
16121   VERIFY_USABLE_WCROOT(wcroot);
16122
16123   q = apr_pcalloc(result_pool, sizeof(*q));
16124
16125   SVN_ERR_ASSERT(wcroot->sdb);
16126
16127   q->wcroot = wcroot;
16128   q->items = apr_array_make(result_pool, 64,
16129                             sizeof(commit_queue_item_t*));
16130   q->have_recurse = FALSE;
16131
16132   *queue = q;
16133   return SVN_NO_ERROR;
16134 }
16135
16136 svn_error_t *
16137 svn_wc__db_commit_queue_add(svn_wc__db_commit_queue_t *queue,
16138                             const char *local_abspath,
16139                             svn_boolean_t recurse,
16140                             svn_boolean_t is_commited,
16141                             svn_boolean_t remove_lock,
16142                             svn_boolean_t remove_changelist,
16143                             const svn_checksum_t *new_sha1_checksum,
16144                             apr_hash_t *new_dav_cache,
16145                             apr_pool_t *result_pool,
16146                             apr_pool_t *scratch_pool)
16147 {
16148   commit_queue_item_t *cqi;
16149   const char *local_relpath;
16150
16151   local_relpath = svn_dirent_skip_ancestor(queue->wcroot->abspath,
16152                                            local_abspath);
16153
16154   if (! local_relpath)
16155     return svn_error_createf(
16156                 SVN_ERR_WC_PATH_NOT_FOUND, NULL,
16157                 _("The path '%s' is not in the working copy '%s'"),
16158                 svn_dirent_local_style(local_abspath, scratch_pool),
16159                 svn_dirent_local_style(queue->wcroot->abspath, scratch_pool));
16160
16161   cqi = apr_pcalloc(result_pool, sizeof(*cqi));
16162   cqi->local_relpath = local_relpath;
16163   cqi->recurse = recurse;
16164   cqi->committed = is_commited;
16165   cqi->remove_lock = remove_lock;
16166   cqi->remove_changelist = remove_changelist;
16167   cqi->new_sha1_checksum = new_sha1_checksum;
16168   cqi->new_dav_cache = new_dav_cache;
16169
16170   queue->have_recurse |= recurse;
16171
16172   APR_ARRAY_PUSH(queue->items, commit_queue_item_t *) = cqi;
16173   return SVN_NO_ERROR;
16174 }
16175
16176 /*** Finishing updates and commits. ***/
16177
16178 /* Post process an item that is committed in the repository. Collapse layers into
16179  * BASE. Queue work items that will finish a commit of the file or directory
16180  * LOCAL_ABSPATH in DB:
16181  */
16182 static svn_error_t *
16183 process_committed_leaf(svn_wc__db_t *db,
16184                        svn_wc__db_wcroot_t *wcroot,
16185                        const char *local_relpath,
16186                        svn_boolean_t via_recurse,
16187                        svn_wc__db_status_t status,
16188                        svn_node_kind_t kind,
16189                        svn_boolean_t prop_mods,
16190                        const svn_checksum_t *old_checksum,
16191                        svn_revnum_t new_revnum,
16192                        apr_time_t new_changed_date,
16193                        const char *new_changed_author,
16194                        apr_hash_t *new_dav_cache,
16195                        svn_boolean_t remove_lock,
16196                        svn_boolean_t remove_changelist,
16197                        const svn_checksum_t *checksum,
16198                        apr_pool_t *scratch_pool)
16199 {
16200   svn_revnum_t new_changed_rev = new_revnum;
16201   svn_skel_t *work_item = NULL;
16202
16203   {
16204     const char *lock_relpath;
16205     svn_boolean_t locked;
16206
16207     if (kind == svn_node_dir)
16208       lock_relpath = local_relpath;
16209     else
16210       lock_relpath = svn_relpath_dirname(local_relpath, scratch_pool);
16211
16212     SVN_ERR(svn_wc__db_wclock_owns_lock_internal(&locked, wcroot,
16213                                                  lock_relpath, FALSE,
16214                                                  scratch_pool));
16215
16216     if (!locked)
16217       return svn_error_createf(SVN_ERR_WC_NOT_LOCKED, NULL,
16218                              _("No write-lock in '%s'"),
16219                              path_for_error_message(wcroot, local_relpath,
16220                                                     scratch_pool));
16221
16222     SVN_ERR(flush_entries(wcroot, lock_relpath, svn_depth_empty,
16223                           scratch_pool));
16224   }
16225
16226   if (status == svn_wc__db_status_not_present)
16227     {
16228       /* We are committing the leaf of a copy operation.
16229          We leave the not-present marker to allow pulling in excluded
16230          children of a copy.
16231
16232          The next update will remove the not-present marker. */
16233
16234       return SVN_NO_ERROR;
16235     }
16236
16237   SVN_ERR_ASSERT(status == svn_wc__db_status_normal
16238                  || status == svn_wc__db_status_incomplete
16239                  || status == svn_wc__db_status_added
16240                  || status == svn_wc__db_status_deleted);
16241
16242   if (kind != svn_node_dir
16243       && status != svn_wc__db_status_deleted)
16244     {
16245       /* If we sent a delta (meaning: post-copy modification),
16246          then this file will appear in the queue and so we should have
16247          its checksum already. */
16248       if (checksum == NULL)
16249         {
16250           /* It was copied and not modified. We must have a text
16251              base for it. And the node should have a checksum. */
16252           SVN_ERR_ASSERT(old_checksum != NULL);
16253
16254           checksum = old_checksum;
16255
16256           /* Is the node completely unmodified and are we recursing? */
16257           if (via_recurse && !prop_mods)
16258             {
16259               /* If a copied node itself is not modified, but the op_root of
16260                  the copy is committed we have to make sure that changed_rev,
16261                  changed_date and changed_author don't change or the working
16262                  copy used for committing will show different last modified
16263                  information then a clean checkout of exactly the same
16264                  revisions. (Issue #3676) */
16265
16266               SVN_ERR(svn_wc__db_read_info_internal(
16267                                            NULL, NULL, NULL, NULL, NULL,
16268                                            &new_changed_rev,
16269                                            &new_changed_date,
16270                                            &new_changed_author, NULL, NULL,
16271                                            NULL, NULL, NULL, NULL, NULL,
16272                                            NULL, NULL, NULL, NULL,
16273                                            NULL, NULL, NULL, NULL,
16274                                            NULL, NULL,
16275                                            wcroot, local_relpath,
16276                                            scratch_pool, scratch_pool));
16277             }
16278         }
16279
16280       SVN_ERR(svn_wc__wq_build_file_commit(&work_item,
16281                                            db, svn_dirent_join(wcroot->abspath,
16282                                                                local_relpath,
16283                                                                scratch_pool),
16284                                            prop_mods,
16285                                            scratch_pool, scratch_pool));
16286     }
16287
16288   /* The new text base will be found in the pristine store by its checksum. */
16289   SVN_ERR(commit_node(wcroot, local_relpath,
16290                       new_revnum, new_changed_rev,
16291                       new_changed_date, new_changed_author,
16292                       checksum,
16293                       new_dav_cache,
16294                       !remove_changelist,
16295                       !remove_lock,
16296                       work_item,
16297                       scratch_pool));
16298
16299   return SVN_NO_ERROR;
16300 }
16301
16302 /** Internal helper for svn_wc_process_committed_queue2().
16303  * Bump a commit item, collapsing local changes with the new repository
16304  * information to a new BASE node.
16305  *
16306  * @a new_date is the (server-side) date of the new revision, or 0.
16307  *
16308  * @a rev_author is the (server-side) author of the new
16309  * revision; it may be @c NULL.
16310  *
16311  * @a new_dav_cache is a hash of all the new dav properties for LOCAL_RELPATH.
16312  *
16313  * If @a remove_lock is set, release any user locks on @a
16314  * local_abspath; otherwise keep them during processing.
16315  *
16316  * If @a remove_changelist is set, clear any changeset assignments
16317  * from @a local_abspath; otherwise, keep such assignments.
16318  *
16319  * If @a new_sha1_checksum is non-NULL, use it to identify the node's pristine
16320  * text.
16321  *
16322  * Set TOP_OF_RECURSE to TRUE to show that this the top of a possibly
16323  * recursive commit operation. (Part of the legacy recurse handling)
16324  */
16325 static svn_error_t *
16326 process_committed_internal(svn_wc__db_t *db,
16327                            svn_wc__db_wcroot_t *wcroot,
16328                            const char *local_relpath,
16329                            svn_boolean_t recurse,
16330                            svn_boolean_t top_of_recurse,
16331                            svn_revnum_t new_revnum,
16332                            apr_time_t new_date,
16333                            const char *rev_author,
16334                            apr_hash_t *new_dav_cache,
16335                            svn_boolean_t remove_lock,
16336                            svn_boolean_t remove_changelist,
16337                            const svn_checksum_t *new_sha1_checksum,
16338                            apr_hash_t *items_by_relpath,
16339                            apr_pool_t *scratch_pool)
16340 {
16341   svn_wc__db_status_t status;
16342   svn_node_kind_t kind;
16343   const svn_checksum_t *old_checksum;
16344   svn_boolean_t prop_mods;
16345
16346   SVN_ERR(svn_wc__db_read_info_internal(&status, &kind, NULL, NULL, NULL, NULL, NULL,
16347                                         NULL, NULL, &old_checksum, NULL, NULL,
16348                                         NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
16349                                         NULL, &prop_mods, NULL, NULL, NULL,
16350                                         wcroot, local_relpath,
16351                                         scratch_pool, scratch_pool));
16352
16353   /* NOTE: be wary of making crazy semantic changes in this function, since
16354      svn_wc_process_committed4() calls this.  */
16355
16356   SVN_ERR(process_committed_leaf(db, wcroot, local_relpath, !top_of_recurse,
16357                                  status, kind, prop_mods, old_checksum,
16358                                  new_revnum, new_date, rev_author,
16359                                  new_dav_cache,
16360                                  remove_lock, remove_changelist,
16361                                  new_sha1_checksum,
16362                                  scratch_pool));
16363
16364   /* Only check for recursion on nodes that have children */
16365   if (kind != svn_node_dir
16366       || status == svn_wc__db_status_not_present
16367       || status == svn_wc__db_status_excluded
16368       || status == svn_wc__db_status_server_excluded
16369       /* Node deleted -> then no longer a directory */
16370       || status == svn_wc__db_status_deleted)
16371     {
16372       return SVN_NO_ERROR;
16373     }
16374
16375   if (recurse)
16376     {
16377       const apr_array_header_t *children;
16378       apr_pool_t *iterpool = svn_pool_create(scratch_pool);
16379       int i;
16380
16381       /* Read PATH's entries;  this is the absolute path. */
16382       SVN_ERR(gather_children(&children, wcroot, local_relpath,
16383                               STMT_SELECT_NODE_CHILDREN, -1,
16384                               scratch_pool, iterpool));
16385
16386       /* Recursively loop over all children. */
16387       for (i = 0; i < children->nelts; i++)
16388         {
16389           const char *name = APR_ARRAY_IDX(children, i, const char *);
16390           const char *this_relpath;
16391           const commit_queue_item_t *cqi;
16392
16393           svn_pool_clear(iterpool);
16394
16395           this_relpath = svn_dirent_join(local_relpath, name, iterpool);
16396
16397           new_sha1_checksum = NULL;
16398           cqi = svn_hash_gets(items_by_relpath, this_relpath);
16399
16400           if (cqi != NULL)
16401             new_sha1_checksum = cqi->new_sha1_checksum;
16402
16403           /* Recurse.  Pass NULL for NEW_DAV_CACHE, because the
16404              ones present in the current call are only applicable to
16405              this one committed item. */
16406           SVN_ERR(process_committed_internal(
16407                     db, wcroot, this_relpath,
16408                     TRUE /* recurse */,
16409                     FALSE /* top_of_recurse */,
16410                     new_revnum, new_date,
16411                     rev_author,
16412                     NULL /* new_dav_cache */,
16413                     FALSE /* remove_lock */,
16414                     remove_changelist,
16415                     new_sha1_checksum,
16416                     items_by_relpath,
16417                     iterpool));
16418         }
16419
16420       svn_pool_destroy(iterpool);
16421     }
16422
16423   return SVN_NO_ERROR;
16424 }
16425
16426 /* Return TRUE if any item of QUEUE is a parent of ITEM and will be
16427    processed recursively, return FALSE otherwise.
16428
16429    The algorithmic complexity of this search implementation is O(queue
16430    length), but it's quite quick.
16431 */
16432 static svn_boolean_t
16433 have_recursive_parent(const apr_array_header_t *all_items,
16434                       const commit_queue_item_t *item,
16435                       apr_pool_t *scratch_pool)
16436 {
16437   const char *local_relpath = item->local_relpath;
16438   int i;
16439
16440   for (i = 0; i < all_items->nelts; i++)
16441     {
16442       const commit_queue_item_t *qi
16443         = APR_ARRAY_IDX(all_items, i, const commit_queue_item_t *);
16444
16445       if (qi == item)
16446         continue;
16447
16448       if (qi->recurse && svn_relpath_skip_ancestor(qi->local_relpath,
16449                                                    local_relpath))
16450         {
16451           return TRUE;
16452         }
16453     }
16454
16455   return FALSE;
16456 }
16457
16458 /* Compare function for svn_sort__array */
16459 static int
16460 compare_queue_items(const void *v1,
16461                     const void *v2)
16462 {
16463   const commit_queue_item_t *cqi1
16464               = *(const commit_queue_item_t **)v1;
16465   const commit_queue_item_t *cqi2
16466               = *(const commit_queue_item_t **)v2;
16467
16468   return svn_path_compare_paths(cqi1->local_relpath, cqi2->local_relpath);
16469 }
16470
16471 /* Internal, locked version of svn_wc__db_process_commit_queue */
16472 static svn_error_t *
16473 db_process_commit_queue(svn_wc__db_t *db,
16474                         svn_wc__db_commit_queue_t *queue,
16475                         svn_revnum_t new_revnum,
16476                         apr_time_t new_date,
16477                         const char *new_author,
16478                         apr_pool_t *scratch_pool)
16479 {
16480   apr_hash_t *items_by_relpath = NULL;
16481   int j;
16482   apr_pool_t *iterpool = svn_pool_create(scratch_pool);
16483
16484   svn_sort__array(queue->items, compare_queue_items);
16485
16486   if (queue->have_recurse)
16487     {
16488       items_by_relpath = apr_hash_make(scratch_pool);
16489
16490       for (j = 0; j < queue->items->nelts; j++)
16491         {
16492           commit_queue_item_t *cqi
16493             = APR_ARRAY_IDX(queue->items, j, commit_queue_item_t *);
16494
16495           svn_hash_sets(items_by_relpath, cqi->local_relpath, cqi);
16496         }
16497     }
16498
16499   for (j = 0; j < queue->items->nelts; j++)
16500     {
16501       commit_queue_item_t *cqi
16502         = APR_ARRAY_IDX(queue->items, j, commit_queue_item_t *);
16503
16504       svn_pool_clear(iterpool);
16505
16506       /* Skip this item if it is a child of a recursive item, because it has
16507          been (or will be) accounted for when that recursive item was (or
16508          will be) processed. */
16509       if (queue->have_recurse && have_recursive_parent(queue->items, cqi,
16510                                                        iterpool))
16511         continue;
16512
16513       if (!cqi->committed)
16514         {
16515           if (cqi->remove_lock)
16516             {
16517               svn_skel_t *work_item;
16518
16519               SVN_ERR(svn_wc__wq_build_sync_file_flags(
16520                                                     &work_item,
16521                                                     db,
16522                                                     svn_dirent_join(
16523                                                         queue->wcroot->abspath,
16524                                                         cqi->local_relpath,
16525                                                         iterpool),
16526                                                     iterpool, iterpool));
16527
16528               lock_remove_txn(queue->wcroot, cqi->local_relpath, work_item,
16529                               iterpool);
16530             }
16531           if (cqi->remove_changelist)
16532             SVN_ERR(svn_wc__db_op_set_changelist(db,
16533                                                  svn_dirent_join(
16534                                                         queue->wcroot->abspath,
16535                                                         cqi->local_relpath,
16536                                                         iterpool),
16537                                                  NULL, NULL,
16538                                                  svn_depth_empty,
16539                                                  NULL, NULL, /* notify */
16540                                                  NULL, NULL, /* cancel */
16541                                                  iterpool));
16542         }
16543       else
16544         {
16545           SVN_ERR(process_committed_internal(
16546                                   db, queue->wcroot, cqi->local_relpath,
16547                                   cqi->recurse,
16548                                   TRUE /* top_of_recurse */,
16549                                   new_revnum, new_date, new_author,
16550                                   cqi->new_dav_cache,
16551                                   cqi->remove_lock,
16552                                   cqi->remove_changelist,
16553                                   cqi->new_sha1_checksum,
16554                                   items_by_relpath,
16555                                   iterpool));
16556         }
16557     }
16558
16559   svn_pool_destroy(iterpool);
16560
16561   return SVN_NO_ERROR;
16562 }
16563
16564 svn_error_t *
16565 svn_wc__db_process_commit_queue(svn_wc__db_t *db,
16566                                 svn_wc__db_commit_queue_t *queue,
16567                                 svn_revnum_t new_revnum,
16568                                 apr_time_t new_date,
16569                                 const char *new_author,
16570                                 apr_pool_t *scratch_pool)
16571 {
16572   SVN_WC__DB_WITH_TXN(db_process_commit_queue(db, queue,
16573                                               new_revnum, new_date,
16574                                               new_author, scratch_pool),
16575                         queue->wcroot);
16576
16577   return SVN_NO_ERROR;
16578 }
16579
16580 svn_error_t *
16581 svn_wc__find_repos_node_in_wc(apr_array_header_t **local_abspath_list,
16582                               svn_wc__db_t *db,
16583                               const char *wri_abspath,
16584                               const char *repos_relpath,
16585                               apr_pool_t *result_pool,
16586                               apr_pool_t *scratch_pool)
16587 {
16588   svn_wc__db_wcroot_t *wcroot;
16589   const char *wri_relpath;
16590   svn_sqlite__stmt_t *stmt;
16591   svn_boolean_t have_row;
16592
16593   SVN_ERR_ASSERT(svn_dirent_is_absolute(wri_abspath));
16594
16595   SVN_ERR(svn_wc__db_wcroot_parse_local_abspath(&wcroot, &wri_relpath, db,
16596                                                  wri_abspath, scratch_pool,
16597                                                  scratch_pool));
16598   VERIFY_USABLE_WCROOT(wcroot);
16599
16600   SVN_ERR(svn_sqlite__get_statement(&stmt, wcroot->sdb,
16601                                     STMT_FIND_REPOS_PATH_IN_WC));
16602   SVN_ERR(svn_sqlite__bindf(stmt, "is", wcroot->wc_id, repos_relpath));
16603   SVN_ERR(svn_sqlite__step(&have_row, stmt));
16604
16605   *local_abspath_list = apr_array_make(result_pool, have_row ? 1 : 0,
16606                                        sizeof(const char*));
16607   while (have_row)
16608     {
16609       const char *local_relpath;
16610       const char *local_abspath;
16611
16612       local_relpath = svn_sqlite__column_text(stmt, 0, NULL);
16613       local_abspath = svn_dirent_join(wcroot->abspath, local_relpath,
16614                                       result_pool);
16615       APR_ARRAY_PUSH(*local_abspath_list, const char *) = local_abspath;
16616
16617       SVN_ERR(svn_sqlite__step(&have_row, stmt));
16618     }
16619     
16620   return svn_error_trace(svn_sqlite__reset(stmt));
16621 }
16622