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