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